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 menjelaskan sistem komunikasi antar-proses (IPC) Trusty, termasuk komunikasi dengan dunia yang tidak aman. Perangkat lunak yang berjalan di prosesor utama dapat menggunakan Trusty API untuk terhubung ke aplikasi/layanan tepercaya dan bertukar pesan apa pun dengan mereka seperti layanan jaringan melalui IP. Terserah aplikasi untuk menentukan format data dan semantik dari pesan menggunakan protokol tingkat aplikasi. Pengiriman pesan yang andal dijamin oleh infrastruktur Trusty yang mendasarinya (dalam bentuk driver berjalan di prosesor utama), dan komunikasinya benar-benar asinkron.
Port dan saluran
Port digunakan oleh aplikasi Trusty untuk mengekspos endpoint layanan dalam bentuk
dari jalur bernama yang terhubung dengan klien. Ini memberikan konfigurasi berbasis string
ID layanan yang akan digunakan klien. Konvensi penamaan adalah gaya DNS terbalik
penamaan, misalnya com.google.servicename
.
Ketika klien terhubung ke porta, klien menerima saluran untuk berinteraksi dengan layanan. Layanan harus menerima koneksi masuk, dan kapan koneksi maka hal itu juga akan menerima sebuah saluran. Pada intinya, porta digunakan untuk mencari layanan dan kemudian komunikasi terjadi melalui pasangan saluran yang terhubung (yaitu, instance koneksi pada port). Ketika klien terhubung ke sebuah porta, sistem simetris, koneksi dua arah dibuat. Dengan menggunakan jalur {i> full-duplex<i} ini, klien dan server dapat bertukar pesan arbitrer hingga kedua belah pihak memutuskan untuk memutus koneksi.
Hanya aplikasi tepercaya sisi aman atau modul kernel Trusty yang dapat membuat porta. Aplikasi yang berjalan pada sisi yang tidak aman (di dunia normal) dapat hanya terhubung ke layanan yang dipublikasikan oleh sisi aman.
Tergantung pada persyaratannya, aplikasi tepercaya bisa menjadi klien dan server pada saat yang sama. Aplikasi tepercaya yang memublikasikan layanan (sebagai server) mungkin perlu terhubung ke layanan lain (sebagai klien).
Menangani API
Nama sebutan channel adalah bilangan bulat tanpa label yang mewakili resource seperti port dan saluran, mirip dengan deskriptor file di UNIX. Setelah nama sebutan channel dibuat, ditempatkan ke dalam tabel nama sebutan channel 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 channel hanya valid dalam konteks aplikasi. Sebuah aplikasi harus
tidak meneruskan nilai handle ke aplikasi lain kecuali secara eksplisit
yang ditentukan. Hanya nilai nama sebutan channel yang harus ditafsirkan dengan membandingkannya dengan
INVALID_IPC_HANDLE #define,
yang dapat digunakan aplikasi sebagai
menunjukkan bahwa handle tidak valid atau tidak ditetapkan.
set_cookie()
Mengaitkan data pribadi yang disediakan 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
[di] cookie
: Pointer ke data ruang pengguna arbitrer di aplikasi Trusty
[retval]: NO_ERROR
jika berhasil, < 0
kode error jika tidak berhasil
Panggilan ini berguna untuk menangani peristiwa saat terjadi di lain waktu setelah nama sebutan channel telah dibuat. Mekanisme penanganan peristiwa menyediakan handle dan cookie-nya kembali ke pengendali peristiwa.
Nama sebutan channel dapat ditunggu untuk peristiwa dengan menggunakan panggilan wait()
.
tunggu()
Menunggu peristiwa terjadi pada nama sebutan channel tertentu selama jangka waktu tertentu.
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 merepresentasikan
peristiwa yang terjadi pada nama sebutan channel ini
[in] timeout_msecs
: Nilai waktu tunggu dalam milidetik; suatu
nilai -1 adalah waktu tunggu yang tidak terbatas
[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 telah terjadi; < 0
untuk error lainnya
Setelah berhasil (retval == NO_ERROR
), panggilan wait()
mengisi struktur uevent_t
tertentu 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 sebenarnya 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()
) ditetapkan
Peristiwa berikut hanya relevan untuk channel:
IPC_HANDLE_POLL_HUP
- menunjukkan bahwa channel telah ditutup oleh pembandingIPC_HANDLE_POLL_MSG
- menunjukkan bahwa ada pesan yang menunggu keputusan untuk saluran iniIPC_HANDLE_POLL_SEND_UNBLOCKED
- menunjukkan bahwa sebelumnya penelepon yang diblokir pengiriman dapat mencoba mengirim pesan lagi (lihat deskripsisend_msg()
untuk detailnya)
Pengendali peristiwa harus siap untuk menangani kombinasi tindakan kejadian, karena beberapa bit dapat ditetapkan secara bersamaan. Misalnya, untuk tetap ada kemungkinan pesan tertunda dan koneksi ditutup oleh rekan Anda secara bersamaan.
Sebagian besar acara bersifat melekat. Kondisi tersebut tetap ada selama kondisi yang mendasarinya
tetap ada (misalnya, semua pesan yang tertunda diterima dan koneksi tertunda
semua permintaan akan ditangani). Namun terdapat pengecualian
peristiwa IPC_HANDLE_POLL_SEND_UNBLOCKED
, yang
dihapus setelah dibaca dan aplikasi hanya
memiliki satu kesempatan untuk
menanganinya.
Nama sebutan channel dapat dihancurkan dengan memanggil metode close()
.
tutup()
Menghancurkan resource yang terkait dengan handle yang ditentukan dan menghapusnya dari tabel {i>handle<i}.
long close(uint32_t handle_id);
[in] handle_id
: Tangani untuk menghancurkan
[retval]: 0 jika berhasil; error negatif.
API Server
Server dimulai dengan membuat satu atau beberapa port bernama yang mewakili endpoint layanannya. Setiap port diwakili oleh handle.
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). Ini
nama harus unik di seluruh sistem; upaya untuk membuat duplikasi akan gagal.
[in] num_recv_bufs
: Jumlah maksimum buffer yang dimiliki channel
porta ini dapat dialokasikan sebelumnya untuk
memfasilitasi pertukaran data dengan klien. Buffering dihitung
secara terpisah untuk data yang berjalan dua arah, jadi menentukan 1 di sini berarti 1
{i>buffer <i}send dan 1{i> <i}menerima telah dialokasikan sebelumnya. Secara umum, jumlah buffer
diperlukan tergantung pada perjanjian
protokol di tingkat yang lebih tinggi antara klien dan
server tertentu. Jumlahnya bisa paling sedikit 1 dalam kasus protokol yang sangat sinkron
(mengirim pesan, menerima balasan sebelum mengirim pesan lain). Tapi jumlahnya bisa
{i>more<i} jika klien berharap untuk mengirim
lebih dari satu pesan sebelum balasan dapat
muncul (misalnya, satu pesan sebagai prolog dan pesan lainnya sebagai perintah aktual). Tujuan
set buffer yang dialokasikan adalah per saluran, jadi dua koneksi terpisah (saluran)
akan memiliki set buffer terpisah.
[in] recv_buf_size
: Ukuran maksimum setiap buffer individual dalam
di atas pengaturan {i>buffer<i}. Nilai ini adalah
bergantung pada protokol dan secara efektif membatasi ukuran pesan maksimum yang dapat dipertukarkan
dengan pembanding
[in] flags
: Kombinasi flag yang menentukan perilaku port tambahan
Nilai ini harus berupa kombinasi dari 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]: Penanganan ke port yang dibuat jika tidak negatif atau error spesifik jika negatif
Server kemudian mencari tahu
daftar {i>port handle<i} untuk koneksi masuk
menggunakan panggilan wait()
. Setelah menerima koneksi
permintaan yang ditunjukkan oleh bit IPC_HANDLE_POLL_READY
yang ditetapkan dalam
kolom event
dari struktur uevent_t
,
server harus memanggil accept()
untuk menyelesaikan pembuatan koneksi dan membuat
saluran (diwakili oleh
nama sebutan channel lain) yang dapat ditelusuri untuk
pesan masuk.
terima()
Menerima koneksi masuk dan menangani channel.
long accept(uint32_t handle_id, uuid_t *peer_uuid);
[in] handle_id
: Handle yang mewakili port yang telah dihubungkan oleh klien
[out] peer_uuid
: Pointer ke struktur uuid_t
yang akan
diisi dengan UUID
aplikasi klien yang terhubung. Ini
akan diatur ke semua nol jika koneksi berasal dari dunia yang tidak aman.
[retval]: Penanganan ke saluran (jika tidak negatif) tempat server dapat bertukar pesan dengan klien (atau sebaliknya, kode kesalahan)
API Klien
Bagian ini berisi metode di Client API.
Metode di Client API
hubungkan()
Memulai koneksi ke porta 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 opsional
[retval]: Nama sebutan channel tempat pesan dapat ditukarkan dengan server; error jika negatif
Jika tidak ada flags
yang ditentukan (parameter flags
disetel ke 0), memanggil connect()
akan memulai koneksi sinkron
ke porta tertentu yang segera
mengembalikan pesan {i>error<i} jika porta tidak ada, dan membuat blok sampai
server sebaliknya menerima koneksi.
Perilaku ini dapat diubah dengan menentukan kombinasi dari dua nilai, dijelaskan di bawah ini:
enum { IPC_CONNECT_WAIT_FOR_PORT = 0x1, IPC_CONNECT_ASYNC = 0x2, };
IPC_CONNECT_WAIT_FOR_PORT
- memaksa connect()
untuk menunggu jika porta yang
ditentukan tidak segera ada saat dieksekusi,
alih-alih langsung gagal.
IPC_CONNECT_ASYNC
- jika ditetapkan, akan memulai koneksi asinkron. Channel
aplikasi harus memeriksa
nama sebutan channel yang ditampilkan (dengan memanggil wait()
untuk
peristiwa penyelesaian koneksi yang ditunjukkan oleh IPC_HANDLE_POLL_READY
yang ditetapkan di kolom event pada struktur uevent_t
sebelum memulai
operasional normal.
API Perpesanan
Panggilan Messaging API memungkinkan pengiriman dan pembacaan pesan melalui koneksi yang sudah dibuat sebelumnya (saluran). Panggilan Messaging API adalah hal yang sama untuk server dan klien.
Klien menerima nama sebutan channel ke channel dengan mengeluarkan connect()
dan server mendapatkan nama sebutan channel dari panggilan accept()
,
yang dijelaskan di atas.
Struktur pesan Tepercaya
Seperti ditunjukkan di bawah ini, pesan yang dipertukarkan oleh Trusty API memiliki , menyerahkannya kepada server dan klien untuk menyetujui semantik konten aktual:
/* * 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
beberapa {i>buffer<i} tidak berdekatan yang diwakili oleh
array struktur iovec_t
. Trusty melakukan {i>scatter-mengumpulkan<i}
membaca dan menulis ke blok ini
menggunakan array iov
. Konten {i>buffer<i} yang dapat dijelaskan
oleh array iov
sepenuhnya bersifat arbitrer.
Metode di Messaging API
send_msg()
Mengirim pesan melalui channel tertentu.
long send_msg(uint32_t handle, ipc_msg_t *msg);
[in] handle
: Nama sebutan channel yang digunakan untuk mengirim pesan
[in] msg
: Pointer ke ipc_msg_t structure
yang menjelaskan pesan
[retval]: Jumlah total byte yang dikirimkan saat berhasil; error negatif.
Jika klien (atau server) mencoba
mengirim pesan melalui saluran dan
jika tidak ada ruang di antrean pesan pembanding tujuan, saluran mungkin
memasuki status diblokirnya pengiriman (hal ini tidak boleh terjadi untuk
protokol permintaan/balas namun mungkin terjadi dalam kasus yang lebih rumit) yang
ditunjukkan dengan menampilkan kode error ERR_NOT_ENOUGH_BUFFER
.
Dalam kasus semacam ini, pemanggil harus menunggu hingga peer membebaskan beberapa
dalam antrian penerimaannya dengan
mengambil penanganan dan penghentian pesan,
ditunjukkan oleh bit IPC_HANDLE_POLL_SEND_UNBLOCKED
yang ditetapkan dalam
kolom event
dari struktur uevent_t
ditampilkan oleh panggilan wait()
.
get_msg()
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
: Nama sebutan channel tempat pesan baru harus diambil
[out] msg_info
: Struktur informasi pesan yang dideskripsikan 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 rangkaian pesan yang belum diproses, dan panjang total setiap pesan diisi. Jika dikonfigurasi dan diizinkan oleh , bisa ada beberapa pesan yang belum terselesaikan (dibuka) sekaligus untuk saluran tertentu.
[retval]: NO_ERROR
saat berhasil; error negatif.
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 yang digunakan untuk membaca pesan
[in] msg_id
: ID pesan yang akan dibaca
[in] offset
: Membuat offset ke pesan yang akan mulai dibaca
[out] msg
: Pointer ke struktur ipc_msg_t
yang menjelaskan
sekumpulan {i>buffer<i} yang digunakan
untuk menyimpan pesan masuk
data
[retval]: Jumlah total byte yang disimpan dalam buffer msg
di
kesuksesan; error negatif.
Metode read_msg
dapat dipanggil beberapa kali mulai dari
jawaban 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 masuk
[in] msg_id
: ID pesan yang dihentikan
[retval]: NO_ERROR
saat berhasil; error negatif.
Konten pesan tidak dapat diakses setelah pesan dihentikan dan buffer yang ditempati telah dibebaskan.
API Deskripsi File
File Descriptor API mencakup read()
, write()
,
dan ioctl()
. Semua panggilan ini dapat beroperasi pada kumpulan file (statis) yang telah ditetapkan sebelumnya
deskripsi biasanya diwakili
oleh angka kecil. Di
dalam implementasi, ruang deskriptor file terpisah dari {i>handle<i} IPC
spasi. File Descriptor API di Trusty berupa
mirip dengan API berbasis deskriptor file tradisional.
Secara default, ada 3 deskriptor file yang telah ditentukan sebelumnya (standar dan terkenal):
- 0 - input standar. Implementasi default input standar
fd
tidak beroperasi (karena aplikasi tepercaya tidak diharapkan memiliki konsol) sehingga membaca, menulis, atau memanggilioctl()
padafd
0 akan menampilkan errorERR_NOT_SUPPORTED
. - 1 - output standar. Data yang ditulis ke {i>output<i} standar dapat dirutekan (bergantung pada
di tingkat debug LK) ke UART dan/atau log memori yang tersedia pada
bergantung pada platform dan konfigurasinya. Log debug non-kritis dan
pesan harus masuk dalam {i>output<i} standar.
read()
danioctl()
adalah metode tanpa pengoperasian dan akan menampilkan errorERR_NOT_SUPPORTED
. - 2 - error standar. Data yang ditulis ke {i>standard error<i} harus dirutekan ke UART
atau log memori yang tersedia di sisi yang
tidak aman, tergantung pada {i>platform<i} dan
konfigurasi Anda. Sebaiknya hanya tulis pesan penting ke
karena streaming ini kemungkinan besar tidak akan di-throttle.
read()
dan Metodeioctl()
tidak dioperasikan dan akan menampilkan errorERR_NOT_SUPPORTED
.
Meskipun kumpulan deskriptor file ini bisa
diperluas untuk mengimplementasikan lebih banyak
fds
(untuk menerapkan ekstensi khusus platform), memperluas kebutuhan deskriptor file
dijalankan dengan hati-hati. Memperluas deskriptor file rentan menimbulkan
menimbulkan konflik dan umumnya tidak direkomendasikan.
Metode di File Descriptor API
read()
Berupaya membaca hingga count
byte data dari deskriptor file yang ditentukan.
long read(uint32_t fd, void *buf, uint32_t count);
[di] fd
: Deskriptor file yang digunakan untuk membaca
[out] buf
: Pointer ke buffer tempat menyimpan data
[in] count
: Jumlah byte maksimum yang akan dibaca
[retval]: Jumlah byte yang dibaca yang ditampilkan; error negatif.
write()
Menulis hingga count
byte data ke deskriptor file yang ditentukan.
long write(uint32_t fd, void *buf, uint32_t count);
[in] fd
: Deskriptor file yang menjadi tujuan penulisan
[out] buf
: Pointer data yang akan ditulis
[in] count
: Jumlah maksimum byte yang akan ditulis
[retval]: Jumlah byte yang ditulis; yang ditampilkan; error negatif.
{i>ioctl()<i}
Memanggil perintah ioctl
yang ditentukan untuk deskriptor file tertentu.
long ioctl(uint32_t fd, uint32_t cmd, void *args);
[di] fd
: Deskriptor file yang akan digunakan untuk memanggil ioctl()
[in] cmd
: Perintah ioctl
[in/out] args
: Pointer ioctl()
argumen
API Lain-Lain
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
: Tergantung platform; teruskan nol untuk default
[in] flags
: Direservasi, harus nol
[out] time
: Pointer ke nilai int64_t
yang digunakan untuk menyimpan waktu saat ini
[retval]: NO_ERROR
saat berhasil; error negatif.
nanosleep()
Menangguhkan eksekusi aplikasi panggilan selama jangka waktu tertentu dan melanjutkannya setelah periode 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
saat berhasil; error negatif.
Contoh server aplikasi tepercaya
Aplikasi contoh berikut menunjukkan penggunaan API di atas. Contoh menciptakan "{i>echo<i}" layanan yang menangani beberapa koneksi masuk dan mencerminkan kembali kepada penelepon semua pesan yang diterima dari klien dari sisi aman atau tidak.
#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()
mengirim 10.000 pesan secara asinkron
dengan "gema" layanan dan nama sebutan channel
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, 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 yang tidak aman.
Lingkungan eksekusi di sisi yang tidak aman ({i>kernel<i} dan {i>user space<i}) sangat berbeda dari lingkungan eksekusi di sisi yang aman. Oleh karena itu, alih-alih satu library untuk kedua lingkungan, ada dua kumpulan API yang berbeda. Dalam kernel, API Klien disediakan oleh {i>driver<i} {i>trusty-ipc<i} dan mendaftarkan node perangkat karakter yang dapat digunakan oleh proses ruang pengguna untuk berkomunikasi dengan layanan yang berjalan di lain.
Ruang pengguna Trusty IPC Client API
Library{i> <i}ruang pengguna Trusty IPC Client API adalah lapisan tipis di atas
node perangkat fd
.
Program ruang pengguna memulai sesi komunikasi
dengan memanggil tipc_connect()
,
menginisialisasi koneksi ke
layanan Trusty tertentu. Secara internal,
panggilan tipc_connect()
akan membuka node perangkat yang ditentukan untuk
mendapatkan deskriptor file dan memanggil TIPC_IOC_CONNECT ioctl()
panggilan 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 *)
Deskriptor file yang dihasilkan hanya bisa digunakan untuk berkomunikasi dengan layanan
yang menjadi tujuan pembuatannya. Deskriptor file harus ditutup dengan
memanggil tipc_close()
saat koneksi tidak diperlukan lagi.
Deskriptor file yang diperoleh dari panggilan tipc_connect()
berperilaku seperti node perangkat karakter; deskriptor file:
- Dapat dialihkan ke mode non-pemblokir jika perlu
- Dapat ditulis menggunakan
write()
standar telepon untuk mengirim pesan ke sisi lain - Dapat dilakukan polling (menggunakan
poll()
panggilan atau panggilanselect()
) ketersediaan pesan masuk sebagai deskriptor file biasa - Dapat dibaca untuk mengambil pesan masuk
Pemanggil mengirim pesan ke layanan Trusty dengan mengeksekusi panggilan tulis untuk
fd
yang ditentukan. Semua data yang diteruskan ke panggilan write()
di atas
diubah menjadi pesan
oleh {i>driver<i} {i>trusty-ipc<i}. Pesannya adalah
dikirim ke sisi yang aman di mana data
ditangani oleh subsistem IPC di
{i>kernel<i} Trusty dan diarahkan ke tujuan
yang tepat lalu dikirimkan ke aplikasi
loop peristiwa sebagai peristiwa IPC_HANDLE_POLL_MSG
pada saluran tertentu
nama sebutan channel. Tergantung pada khususnya,
protokol khusus layanan, layanan Trusty dapat mengirim satu atau beberapa balasan
pesan yang dikirim kembali ke sisi yang tidak aman dan ditempatkan
antrean pesan deskriptor file saluran yang sesuai untuk diambil oleh pengguna
panggilan read()
aplikasi ruang angkasa.
{i>tipc_connect()<i}
Membuka node perangkat tipc
yang ditentukan dan memulai
koneksi ke layanan
Trusty tertentu.
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 dan dihubungkan
[retval]: Deskriptor file yang valid jika berhasil, -1 jika tidak berhasil.
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 Kernel Trusty IPC
Kernel Trusty IPC Client API tersedia untuk driver kernel. Pengguna Trusty IPC API, yang diimplementasikan di atas API ini.
Secara umum, penggunaan standar API ini terdiri dari pemanggil yang membuat
objek struct tipc_chan
dengan menggunakan tipc_create_channel()
fungsi, lalu menggunakan panggilan tipc_chan_connect()
untuk memulai
koneksi ke layanan {i>Trusty IPC<i}
yang berjalan di jaringan
lain. Koneksi ke sisi jarak jauh
dapat diakhiri 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
hal berikut:
- Mendapatkan buffer pesan menggunakan panggilan
tipc_chan_get_txbuf_timeout()
- Menulis pesan, dan
- Mengantrekan pesan menggunakan
tipc_chan_queue_msg()
untuk pengiriman ke layanan Trusty (di sisi aman), yang saluran terhubung
Setelah antrean berhasil, pemanggil akan melupakan buffer pesan
karena {i>buffer<i} pesan pada akhirnya kembali
ke kumpulan {i>buffer <i}gratis setelah
secara jarak jauh (untuk digunakan kembali nanti, untuk pesan lainnya). Pengguna
hanya perlu memanggil tipc_chan_put_txbuf()
jika gagal
mengantrekan buffer tersebut atau
tidak diperlukan lagi.
Pengguna API menerima pesan dari sisi jarak jauh dengan menangani
Callback notifikasi handle_msg()
(yang dipanggil di
konteks antrean kerja rx
trusty-ipc) yang
memberikan pointer ke buffer rx
yang berisi
pesan masuk harus ditangani.
Diharapkan bahwa callback handle_msg()
akan menampilkan pointer ke struct tipc_msg_buf
yang valid.
Ini bisa sama dengan buffer pesan masuk jika ditangani secara lokal
dan tidak diperlukan lagi. Atau, bisa berupa {i>buffer<i}
baru yang diperoleh oleh
panggilan tipc_chan_get_rxbuf()
jika buffer masuk dalam antrean
untuk diproses lebih lanjut. Buffer rx
yang dilepas harus dilacak
dan akhirnya dilepas menggunakan panggilan tipc_chan_put_rxbuf()
saat
sudah tidak diperlukan lagi.
Metode di Kernel Trusty IPC Client API
tipc_create_channel()
Membuat dan mengonfigurasi instance saluran Trusty IPC untuk trusty-ipc.
struct tipc_chan *tipc_create_channel(struct device *dev, const struct tipc_chan_ops *ops, void *cb_arg);
[in] dev
: Pointer ke trust-ipc yang digunakan perangkat
saluran dibuat
[in] ops
: Pointer ke struct tipc_chan_ops
,
dengan khusus penelepon
callback diisi
[in] cb_arg
: Pointer ke data yang akan diteruskan
ke callback tipc_chan_ops
[retval]: Penunjuk ke instance yang baru dibuat dari
struct tipc_chan
saat berhasil,
ERR_PTR(err)
sebaliknya
Secara umum, pemanggil harus memberikan dua callback yang dipanggil secara asinkron kapan 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
tipc_create_channel()
panggilan
[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 ada permintaan koneksi baru atau permintaan pemutusan koneksi untuk saluran yang sebelumnya terhubungTIPC_CHANNEL_SHUTDOWN
- menunjukkan sistem jarak jauh sedang dimatikan, menghentikan semua koneksi secara permanen
struct tipc_msg_buf *(*handle_msg)(void *cb_arg, struct tipc_msg_buf *mb)
Callback dipanggil untuk memberikan notifikasi bahwa pesan baru telah
diterima melalui saluran tertentu:
- [in]
cb_arg
: Pointer ke data yang diteruskan ketipc_create_channel()
panggilan - [in]
mb
: Pointer kestruct tipc_msg_buf
menjelaskan pesan masuk - [retval]: Implementasi callback diharapkan untuk mengembalikan pointer ke
struct tipc_msg_buf
yang dapat berupa pointer sama yang diterima sebagaimb
jika pesan ditangani secara lokal dan tidak diperlukan lagi (atau akan dapat berupa buffer baru yang diperoleh dari panggilantipc_chan_get_rxbuf()
)
{i>tipc_chan_connect()<i}
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
tipc_create_chan()
panggilan
[in] port
: Pointer ke string yang berisi
nama layanan yang akan dihubungkan
[retval]: 0 jika berhasil, error negatif jika tidak berhasil
Pemanggil akan diberitahu ketika tersambung dengan
Callback handle_event
.
tipc_chan_shutdown()
Menghentikan koneksi ke layanan Trusty IPC yang dimulai sebelumnya
dengan panggilan tipc_chan_connect()
.
int tipc_chan_shutdown(struct tipc_chan *chan);
[in] chan
: Pointer ke saluran yang ditampilkan oleh
panggilan tipc_create_chan()
{i>tipc_chan_ anotasi<i}()
Menghancurkan saluran Trusty IPC tertentu.
void tipc_chan_destroy(struct tipc_chan *chan);
[in] chan
: Pointer ke saluran yang ditampilkan oleh
tipc_create_chan()
panggilan
tipc_chan_get_txbuf_timeout()
Memperoleh buffer pesan yang bisa digunakan untuk mengirim data melalui saluran TV Anda. 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 untuk mengantrekan pesan
[in] chan
: Waktu tunggu maksimum untuk menunggu hingga
Buffering tx
tersedia
[retval]: Buffering pesan yang valid saat berhasil,
ERR_PTR(err)
saat error
tipc_chan_queue_msg()
Mengantrekan pesan untuk dikirim melalui jaringan yang ditentukan Saluran IPC tepercaya.
int tipc_chan_queue_msg(struct tipc_chan *chan, struct tipc_msg_buf *mb);
[in] chan
: Pointer ke saluran yang akan mengantrekan pesan
[in] mb:
Penunjuk ke pesan yang akan diantrekan
(diperoleh dari panggilan tipc_chan_get_txbuf_timeout()
)
[retval]: 0 jika berhasil, error negatif jika tidak berhasil
tipc_chan_put_txbuf()
Merilis buffer pesan Tx
yang ditentukan
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
: Pointer ke saluran yang
buffer pesan ini termasuk
[in] mb
: Pointer ke buffer pesan yang akan dilepaskan
[retval]: Tidak ada
tipc_chan_get_rxbuf()
Memperoleh buffer pesan baru yang bisa digunakan untuk menerima pesan melalui saluran tertentu.
struct tipc_msg_buf *tipc_chan_get_rxbuf(struct tipc_chan *chan);
[in] chan
: Pointer ke saluran yang memiliki buffer pesan ini
[retval]: Buffering pesan valid saat berhasil, ERR_PTR(err)
saat error
tipc_chan_put_rxbuf()
Merilis buffer pesan tertentu yang sebelumnya diperoleh
Panggilan tipc_chan_get_rxbuf()
.
void tipc_chan_put_rxbuf(struct tipc_chan *chan, struct tipc_msg_buf *mb);
[in] chan
: Pointer ke saluran yang memiliki buffer pesan ini
[in] mb
: Pointer ke buffering pesan yang akan dilepaskan
[retval]: Tidak ada