Referensi Trusty API

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.

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 pembanding
  • IPC_HANDLE_POLL_MSG - menunjukkan bahwa ada pesan yang menunggu keputusan untuk saluran ini
  • IPC_HANDLE_POLL_SEND_UNBLOCKED - menunjukkan bahwa sebelumnya penelepon yang diblokir pengiriman dapat mencoba mengirim pesan lagi (lihat deskripsi send_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 memanggil ioctl() pada fd 0 akan menampilkan error ERR_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() dan ioctl() adalah metode tanpa pengoperasian dan akan menampilkan error ERR_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 Metode ioctl() tidak dioperasikan dan akan menampilkan error ERR_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 panggilan select()) 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 jauh
  • TIPC_CHANNEL_DISCONNECTED - menunjukkan bahwa sisi jarak jauh menolak ada permintaan koneksi baru atau permintaan pemutusan koneksi untuk saluran yang sebelumnya terhubung
  • TIPC_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 ke tipc_create_channel() panggilan
  • [in] mb: Pointer ke struct 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 sebagai mb jika pesan ditangani secara lokal dan tidak diperlukan lagi (atau akan dapat berupa buffer baru yang diperoleh dari panggilan tipc_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