Referensi API Terpercaya

Trusty menyediakan API untuk mengembangkan dua kelas aplikasi/layanan:

  • Aplikasi atau layanan tepercaya yang berjalan pada prosesor TEE
  • Aplikasi normal/tidak tepercaya yang berjalan pada prosesor utama dan menggunakan layanan yang disediakan oleh aplikasi tepercaya

Trusty API umumnya menggambarkan sistem komunikasi antar-proses (IPC) Trusty, termasuk komunikasi dengan dunia yang tidak aman. Perangkat lunak yang berjalan pada prosesor utama dapat menggunakan Trusty API untuk terhubung ke aplikasi/layanan tepercaya dan bertukar pesan arbitrer dengan aplikasi/layanan tersebut seperti layanan jaringan melalui IP. Terserah pada aplikasi untuk menentukan format data dan semantik pesan-pesan ini menggunakan protokol tingkat aplikasi. Pengiriman pesan yang andal dijamin oleh infrastruktur Trusty yang mendasarinya (dalam bentuk driver yang berjalan pada prosesor utama), dan komunikasi sepenuhnya tidak sinkron.

Port dan saluran

Port digunakan oleh aplikasi Trusty untuk mengekspos titik akhir layanan dalam bentuk jalur bernama yang terhubung dengan klien. Ini memberikan ID layanan sederhana berbasis string untuk digunakan klien. Konvensi penamaannya adalah penamaan bergaya DNS terbalik, misalnya com.google.servicename .

Ketika klien terhubung ke port, klien menerima saluran untuk berinteraksi dengan layanan. Layanan harus menerima sambungan masuk, dan jika menerima sambungan masuk, layanan juga menerima saluran. Intinya, port digunakan untuk mencari layanan dan kemudian komunikasi terjadi melalui sepasang saluran yang terhubung (yaitu, contoh koneksi pada port). Ketika klien terhubung ke port, koneksi dua arah simetris dibuat. Dengan menggunakan jalur dupleks penuh ini, klien dan server dapat bertukar pesan sewenang-wenang hingga kedua belah pihak memutuskan untuk memutuskan koneksi.

Hanya aplikasi tepercaya sisi aman atau modul kernel Terpercaya yang dapat membuat port. Aplikasi yang berjalan di sisi tidak aman (di dunia normal) hanya dapat terhubung ke layanan yang diterbitkan oleh sisi aman.

Bergantung pada kebutuhan, aplikasi tepercaya dapat menjadi klien dan server secara bersamaan. Aplikasi tepercaya yang menerbitkan layanan (sebagai server) mungkin perlu tersambung ke layanan lain (sebagai klien).

Tangani API

Pegangan adalah bilangan bulat tak bertanda tangan yang mewakili sumber daya seperti port dan saluran, mirip dengan deskriptor file di UNIX. Setelah pegangan dibuat, pegangan tersebut ditempatkan ke dalam tabel pegangan khusus aplikasi dan dapat direferensikan nanti.

Penelepon dapat mengaitkan data pribadi dengan pegangan menggunakan metode set_cookie() .

Metode di Handle API

Pegangan hanya valid dalam konteks aplikasi. Suatu aplikasi tidak boleh meneruskan nilai pegangan ke aplikasi lain kecuali ditentukan secara eksplisit. Nilai pegangan saja harus diinterpretasikan dengan membandingkannya dengan INVALID_IPC_HANDLE #define, yang dapat digunakan aplikasi sebagai indikasi bahwa pegangan tidak valid atau tidak disetel.

Mengaitkan data pribadi yang disediakan penelepon dengan pegangan tertentu.

long set_cookie(uint32_t handle, void *cookie)

[in] handle : Pegangan apa pun yang dikembalikan oleh salah satu panggilan API

[dalam] cookie : Penunjuk ke data ruang pengguna sewenang-wenang di aplikasi Trusty

[retval]: NO_ERROR jika berhasil, < 0 kode kesalahan sebaliknya

Panggilan ini berguna untuk menangani peristiwa yang terjadi di lain waktu setelah pegangan dibuat. Mekanisme penanganan event memasok handle dan cookie-nya kembali ke event handler.

Penanganan dapat ditunggu untuk suatu peristiwa dengan menggunakan panggilan wait() .

Tunggu()

Menunggu suatu peristiwa terjadi pada pegangan tertentu untuk jangka waktu tertentu.

long wait(uint32_t handle_id, uevent_t *event, unsigned long timeout_msecs)

[di] handle_id : Pegangan apa pun yang dikembalikan oleh salah satu panggilan API

[out] event : Penunjuk ke struktur yang mewakili peristiwa yang terjadi pada pegangan ini

[dalam] timeout_msecs : Nilai batas waktu dalam milidetik; nilai -1 adalah batas waktu yang tidak terbatas

[retval]: NO_ERROR jika peristiwa valid terjadi dalam interval waktu habis yang ditentukan; ERR_TIMED_OUT jika batas waktu yang ditentukan telah berlalu namun tidak ada peristiwa yang terjadi; < 0 untuk kesalahan lainnya

Setelah berhasil ( retval == NO_ERROR ), panggilan wait() mengisi struktur uevent_t yang ditentukan dengan informasi tentang peristiwa yang terjadi.

typedef struct uevent {
    uint32_t handle; /* handle this event is related to */
    uint32_t event;  /* combination of IPC_HANDLE_POLL_XXX flags */
    void    *cookie; /* cookie associated with this handle */
} uevent_t;

Bidang event berisi kombinasi nilai berikut:

enum {
  IPC_HANDLE_POLL_NONE    = 0x0,
  IPC_HANDLE_POLL_READY   = 0x1,
  IPC_HANDLE_POLL_ERROR   = 0x2,
  IPC_HANDLE_POLL_HUP     = 0x4,
  IPC_HANDLE_POLL_MSG     = 0x8,
  IPC_HANDLE_POLL_SEND_UNBLOCKED = 0x10,
  … more values[TBD]
};

IPC_HANDLE_POLL_NONE - tidak ada acara yang tertunda, penelepon harus memulai kembali penantian

IPC_HANDLE_POLL_ERROR - telah terjadi kesalahan internal yang tidak ditentukan

IPC_HANDLE_POLL_READY - bergantung pada jenis pegangan, sebagai berikut:

  • Untuk port, nilai ini menunjukkan adanya koneksi yang tertunda
  • Untuk saluran, nilai ini menunjukkan bahwa koneksi asinkron (lihat connect() ) telah dibuat

Peristiwa berikut hanya relevan untuk saluran:

  • IPC_HANDLE_POLL_HUP - menunjukkan bahwa saluran telah ditutup oleh rekannya
  • IPC_HANDLE_POLL_MSG - menunjukkan bahwa ada pesan tertunda untuk saluran ini
  • IPC_HANDLE_POLL_SEND_UNBLOCKED - menunjukkan bahwa penelepon yang sebelumnya diblokir pengiriman mungkin mencoba mengirim pesan lagi (lihat deskripsi send_msg() untuk detailnya)

Pengendali peristiwa harus siap menangani kombinasi peristiwa tertentu, karena beberapa bit mungkin disetel pada waktu yang sama. Misalnya, untuk suatu saluran, dimungkinkan untuk memiliki pesan yang tertunda, dan koneksi ditutup oleh rekan pada saat yang bersamaan.

Sebagian besar peristiwa bersifat melekat. Mereka bertahan selama kondisi yang mendasarinya masih ada (misalnya semua pesan yang tertunda diterima dan permintaan koneksi yang tertunda ditangani). Pengecualian adalah kasus peristiwa IPC_HANDLE_POLL_SEND_UNBLOCKED , yang dihapus setelah dibaca dan aplikasi hanya memiliki satu kesempatan untuk menanganinya.

Pegangan dapat dimusnahkan dengan memanggil metode close() .

menutup()

Menghancurkan sumber daya yang terkait dengan pegangan yang ditentukan dan menghapusnya dari tabel pegangan.

long close(uint32_t handle_id);

[dalam] handle_id : Pegangan untuk menghancurkan

[retval]: 0 jika berhasil; kesalahan negatif sebaliknya

API server

Server memulai dengan membuat satu atau lebih port bernama yang mewakili titik akhir layanannya. Setiap port diwakili oleh sebuah pegangan.

Metode di Server API

port_buat()

Membuat port layanan bernama.

long port_create (const char *path, uint num_recv_bufs, size_t recv_buf_size,
uint32_t flags)

[in] path : Nama string port (seperti dijelaskan di atas). Nama ini harus unik di seluruh sistem; upaya untuk membuat duplikat akan gagal.

[in] num_recv_bufs : Jumlah maksimum buffer yang dapat dialokasikan sebelumnya oleh saluran pada port ini untuk memfasilitasi pertukaran data dengan klien. Buffer dihitung secara terpisah untuk data yang dikirim ke dua arah, jadi menentukan 1 di sini berarti 1 buffer pengiriman dan 1 buffer penerimaan telah dialokasikan sebelumnya. Secara umum, jumlah buffer yang dibutuhkan bergantung pada perjanjian protokol tingkat yang lebih tinggi antara klien dan server. Jumlahnya bisa sedikitnya 1 jika protokolnya sangat sinkron (kirim pesan, terima balasan sebelum mengirim pesan lain). Namun jumlahnya bisa lebih banyak jika klien mengharapkan untuk mengirim lebih dari satu pesan sebelum balasan dapat muncul (misalnya, satu pesan sebagai prolog dan pesan lainnya sebagai perintah sebenarnya). Kumpulan buffer yang dialokasikan adalah per saluran, jadi dua koneksi (saluran) terpisah akan memiliki set buffer terpisah.

[dalam] recv_buf_size : Ukuran maksimum setiap buffer individu dalam set buffer di atas. Nilai ini bergantung pada protokol dan secara efektif membatasi ukuran pesan maksimum yang dapat Anda tukarkan dengan rekan

[in] flags : Kombinasi flag yang menentukan perilaku port tambahan

Nilai ini harus merupakan kombinasi dari nilai-nilai berikut:

IPC_PORT_ALLOW_TA_CONNECT - memungkinkan koneksi dari aplikasi aman lainnya

IPC_PORT_ALLOW_NS_CONNECT - memungkinkan koneksi dari dunia yang tidak aman

[retval]: Menangani port yang dibuat jika non-negatif atau kesalahan tertentu jika negatif

Server kemudian melakukan polling daftar pegangan port untuk koneksi masuk menggunakan panggilan wait() . Setelah menerima permintaan koneksi yang ditunjukkan oleh set bit IPC_HANDLE_POLL_READY di bidang event struktur uevent_t , server harus memanggil accept() untuk menyelesaikan pembuatan koneksi dan membuat saluran (diwakili oleh pegangan lain) yang kemudian dapat disurvei untuk pesan masuk .

menerima()

Menerima koneksi masuk dan menangani saluran.

long accept(uint32_t handle_id, uuid_t *peer_uuid);

[di] handle_id : Pegangan yang mewakili port tempat klien terhubung

[out] peer_uuid : Penunjuk ke struktur uuid_t untuk diisi dengan UUID aplikasi klien yang terhubung. Ini akan disetel ke nol semua jika koneksi berasal dari dunia yang tidak aman

[retval]: Menangani saluran (jika non-negatif) di mana server dapat bertukar pesan dengan klien (atau kode kesalahan sebaliknya)

API Klien

Bagian ini berisi metode di API Klien.

Metode di API Klien

Menghubung()

Memulai koneksi ke port yang ditentukan berdasarkan nama.

long connect(const char *path, uint flags);

[di] path : Nama port yang diterbitkan oleh aplikasi Trusty

[in] flags : Menentukan perilaku opsional tambahan

[retval]: Menangani saluran di mana pesan dapat dipertukarkan dengan server; kesalahan jika negatif

Jika tidak ada flags yang ditentukan (parameter flags disetel ke 0), pemanggilan connect() akan memulai koneksi sinkron ke port tertentu yang segera mengembalikan kesalahan jika port tidak ada, dan membuat blok hingga server menerima koneksi .

Perilaku ini dapat diubah dengan menentukan kombinasi dua nilai, dijelaskan di bawah:

enum {
IPC_CONNECT_WAIT_FOR_PORT = 0x1,
IPC_CONNECT_ASYNC = 0x2,
};

IPC_CONNECT_WAIT_FOR_PORT - memaksa panggilan connect() untuk menunggu jika port yang ditentukan tidak segera ada saat eksekusi, bukannya langsung gagal.

IPC_CONNECT_ASYNC - jika disetel, memulai koneksi asinkron. Aplikasi harus melakukan polling untuk pegangan yang dikembalikan (dengan memanggil wait() untuk peristiwa penyelesaian koneksi yang ditunjukkan oleh bit IPC_HANDLE_POLL_READY yang disetel di bidang peristiwa struktur uevent_t sebelum memulai operasi normal.

API Pesan

Panggilan Messaging API memungkinkan pengiriman dan pembacaan pesan melalui koneksi (saluran) yang telah dibuat sebelumnya. Panggilan Messaging API sama untuk server dan klien.

Klien menerima pegangan saluran dengan mengeluarkan panggilan connect() , dan server mendapatkan pegangan saluran dari panggilan accept() , yang dijelaskan di atas.

Struktur pesan yang dapat dipercaya

Seperti yang ditunjukkan berikut ini, pesan yang dipertukarkan oleh Trusty API memiliki struktur minimal, sehingga server dan klien dapat menyetujui semantik konten sebenarnya:

/*
 *  IPC message
 */
typedef struct iovec {
        void   *base;
        size_t  len;
} iovec_t;

typedef struct ipc_msg {
        uint     num_iov; /* number of iovs in this message */
        iovec_t  *iov;    /* pointer to iov array */

        uint     num_handles; /* reserved, currently not supported */
        handle_t *handles;    /* reserved, currently not supported */
} ipc_msg_t;

Sebuah pesan dapat terdiri dari satu atau lebih buffer yang tidak bersebelahan yang diwakili oleh array struktur iovec_t . Trusty melakukan pembacaan dan penulisan scatter-gather ke blok-blok ini menggunakan array iov . Isi buffer yang dapat dijelaskan oleh array iov sepenuhnya berubah-ubah.

Metode di API Pesan

kirim_pesan()

Mengirim pesan melalui saluran tertentu.

long send_msg(uint32_t handle, ipc_msg_t *msg);

[in] handle : Menangani saluran untuk mengirim pesan

[dalam] msg : Penunjuk ke ipc_msg_t structure yang menjelaskan pesan

[retval]: Jumlah total byte yang dikirim jika berhasil; kesalahan negatif sebaliknya

Jika klien (atau server) mencoba mengirim pesan melalui saluran dan tidak ada ruang di antrian pesan rekan tujuan, saluran mungkin memasuki keadaan kirim-diblokir (hal ini tidak boleh terjadi untuk protokol permintaan/balasan sinkron sederhana tetapi mungkin terjadi dalam kasus yang lebih rumit) yang ditunjukkan dengan mengembalikan kode kesalahan ERR_NOT_ENOUGH_BUFFER . Dalam kasus seperti ini, pemanggil harus menunggu hingga peer mengosongkan sebagian ruang di antrean penerimaannya dengan mengambil pesan yang ditangani dan dihentikan, yang ditunjukkan oleh bit IPC_HANDLE_POLL_SEND_UNBLOCKED yang disetel dalam bidang event struktur uevent_t yang dikembalikan oleh panggilan wait() .

dapatkan_pesan()

Mendapatkan informasi meta tentang pesan berikutnya dalam antrean pesan masuk

dari saluran tertentu.

long get_msg(uint32_t handle, ipc_msg_info_t *msg_info);

[in] handle : Menangani saluran di mana pesan baru harus diambil

[keluar] msg_info : Struktur informasi pesan dijelaskan sebagai berikut:

typedef struct ipc_msg_info {
        size_t    len;  /* total message length */
        uint32_t  id;   /* message id */
} ipc_msg_info_t;

Setiap pesan diberi ID unik di seluruh kumpulan pesan yang beredar, dan total panjang setiap pesan diisi. Jika dikonfigurasi dan diizinkan oleh protokol, mungkin ada beberapa pesan yang beredar (dibuka) sekaligus untuk saluran tertentu.

[retval]: NO_ERROR jika berhasil; kesalahan negatif sebaliknya

baca_pesan()

Membaca isi pesan dengan ID yang ditentukan mulai dari offset yang ditentukan.

long read_msg(uint32_t handle, uint32_t msg_id, uint32_t offset, ipc_msg_t
*msg);

[in] handle : Menangani saluran untuk membaca pesan

[dalam] msg_id : ID pesan yang akan dibaca

[in] offset : Diimbangi ke dalam pesan untuk mulai membaca

[keluar] msg : Penunjuk ke struktur ipc_msg_t yang menjelaskan sekumpulan buffer untuk menyimpan data pesan masuk

[retval]: Jumlah total byte yang disimpan dalam buffer msg jika berhasil; kesalahan negatif sebaliknya

Metode read_msg dapat dipanggil beberapa kali dimulai pada offset yang berbeda (tidak harus berurutan) sesuai kebutuhan.

put_msg()

Menghentikan pesan dengan ID tertentu.

long put_msg(uint32_t handle, uint32_t msg_id);

[in] handle : Pegangan saluran tempat pesan tiba

[dalam] msg_id : ID pesan dihentikan

[retval]: NO_ERROR jika berhasil; kesalahan negatif sebaliknya

Konten pesan tidak dapat diakses setelah pesan dihentikan dan buffer yang ditempati telah dibebaskan.

API Deskriptor File

File Descriptor API mencakup panggilan read() , write() , dan ioctl() . Semua panggilan ini dapat beroperasi pada kumpulan deskriptor file yang telah ditentukan sebelumnya (statis) yang biasanya diwakili oleh angka kecil. Dalam implementasi saat ini, ruang deskriptor file terpisah dari ruang pegangan IPC. File Descriptor API di Trusty mirip dengan API berbasis deskriptor file tradisional.

Secara default, ada 3 deskriptor file yang telah ditentukan sebelumnya (standar dan terkenal):

  • 0 - masukan standar. Implementasi default dari input standar fd adalah no-op (karena aplikasi tepercaya tidak diharapkan memiliki konsol interaktif) sehingga membaca, menulis, atau memanggil ioctl() pada fd 0 akan menghasilkan kesalahan ERR_NOT_SUPPORTED .
  • 1 - keluaran standar. Data yang ditulis ke output standar dapat dirutekan (tergantung pada tingkat debug LK) ke UART dan/atau log memori yang tersedia di sisi tidak aman, bergantung pada platform dan konfigurasi. Log dan pesan debug yang tidak penting harus masuk dalam keluaran standar. Metode read() dan ioctl() tidak boleh dijalankan dan akan mengembalikan kesalahan ERR_NOT_SUPPORTED .
  • 2 - kesalahan standar. Data yang ditulis ke kesalahan standar harus dirutekan ke UART atau log memori yang tersedia di sisi tidak aman, bergantung pada platform dan konfigurasi. Disarankan untuk hanya menulis pesan penting ke kesalahan standar, karena aliran ini kemungkinan besar tidak akan dibatasi. Metode read() dan ioctl() tidak boleh dijalankan dan akan mengembalikan kesalahan ERR_NOT_SUPPORTED .

Meskipun kumpulan deskriptor file ini dapat diperluas untuk mengimplementasikan lebih banyak fds (untuk mengimplementasikan ekstensi khusus platform), perluasan deskriptor file perlu dilakukan dengan hati-hati. Memperluas deskriptor file rentan menimbulkan konflik dan umumnya tidak disarankan.

Metode di File Descriptor API

membaca()

Mencoba membaca hingga count byte data dari deskriptor file tertentu.

long read(uint32_t fd, void *buf, uint32_t count);

[di] fd : Deskriptor file untuk dibaca

[keluar] buf : Penunjuk ke buffer untuk menyimpan data

[dalam] count : Jumlah byte maksimum untuk dibaca

[retval]: Mengembalikan jumlah byte yang dibaca; kesalahan negatif sebaliknya

menulis()

Menulis hingga count byte data ke deskriptor file yang ditentukan.

long write(uint32_t fd, void *buf, uint32_t count);

[di] fd : Deskriptor file untuk menulis

[keluar] buf : Penunjuk ke data yang akan ditulis

[dalam] count : Jumlah byte maksimum untuk ditulis

[retval]: Mengembalikan jumlah byte yang ditulis; kesalahan negatif sebaliknya

ioctl()

Memanggil perintah ioctl tertentu untuk deskriptor file tertentu.

long ioctl(uint32_t fd, uint32_t cmd, void *args);

[dalam] fd : Deskriptor file untuk memanggil ioctl()

[dalam] cmd : Perintah ioctl

[masuk/keluar] args : Penunjuk ke argumen ioctl()

API lain-lain

Metode di API Lain-Lain

dapatkan waktu()

Mengembalikan waktu sistem saat ini (dalam nanodetik).

long gettime(uint32_t clock_id, uint32_t flags, int64_t *time);

[dalam] clock_id : Bergantung pada platform; lulus nol untuk default

[dalam] flags : Dicadangkan, harus nol

[out] time : Menunjuk ke nilai int64_t untuk menyimpan waktu saat ini

[retval]: NO_ERROR jika berhasil; kesalahan negatif sebaliknya

tidur nano()

Menunda pelaksanaan aplikasi pemanggil untuk jangka waktu tertentu dan melanjutkannya setelah jangka waktu tersebut.

long nanosleep(uint32_t clock_id, uint32_t flags, uint64_t sleep_time)

[dalam] clock_id : Dicadangkan, harus nol

[dalam] flags : Dicadangkan, harus nol

[dalam] sleep_time : Waktu tidur dalam nanodetik

[retval]: NO_ERROR jika berhasil; kesalahan negatif sebaliknya

Contoh server aplikasi terpercaya

Contoh aplikasi berikut menunjukkan penggunaan API di atas. Sampel membuat layanan "echo" yang menangani beberapa koneksi masuk dan mencerminkan kembali ke pemanggil semua pesan yang diterima dari klien yang berasal dari sisi aman atau tidak aman.

#include <uapi/err.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <trusty_ipc.h>
#define LOG_TAG "echo_srv"
#define TLOGE(fmt, ...) \
    fprintf(stderr, "%s: %d: " fmt, LOG_TAG, __LINE__, ##__VA_ARGS__)

# define MAX_ECHO_MSG_SIZE 64

static
const char * srv_name = "com.android.echo.srv.echo";

static uint8_t msg_buf[MAX_ECHO_MSG_SIZE];

/*
 *  Message handler
 */
static int handle_msg(handle_t chan) {
  int rc;
  struct iovec iov;
  ipc_msg_t msg;
  ipc_msg_info_t msg_inf;

  iov.iov_base = msg_buf;
  iov.iov_len = sizeof(msg_buf);

  msg.num_iov = 1;
  msg.iov = &iov;
  msg.num_handles = 0;
  msg.handles = NULL;

  /* get message info */
  rc = get_msg(chan, &msg_inf);
  if (rc == ERR_NO_MSG)
    return NO_ERROR; /* no new messages */

  if (rc != NO_ERROR) {
    TLOGE("failed (%d) to get_msg for chan (%d)\n",
      rc, chan);
    return rc;
  }

  /* read msg content */
  rc = read_msg(chan, msg_inf.id, 0, &msg);
  if (rc < 0) {
    TLOGE("failed (%d) to read_msg for chan (%d)\n",
      rc, chan);
    return rc;
  }

  /* update number of bytes received */
  iov.iov_len = (size_t) rc;

  /* send message back to the caller */
  rc = send_msg(chan, &msg);
  if (rc < 0) {
    TLOGE("failed (%d) to send_msg for chan (%d)\n",
      rc, chan);
    return rc;
  }

  /* retire message */
  rc = put_msg(chan, msg_inf.id);
  if (rc != NO_ERROR) {
    TLOGE("failed (%d) to put_msg for chan (%d)\n",
      rc, chan);
    return rc;
  }

  return NO_ERROR;
}

/*
 *  Channel event handler
 */
static void handle_channel_event(const uevent_t * ev) {
  int rc;

  if (ev->event & IPC_HANDLE_POLL_MSG) {
    rc = handle_msg(ev->handle);
    if (rc != NO_ERROR) {
      /* report an error and close channel */
      TLOGE("failed (%d) to handle event on channel %d\n",
        rc, ev->handle);
      close(ev->handle);
    }
    return;
  }
  if (ev->event & IPC_HANDLE_POLL_HUP) {
    /* closed by peer. */
    close(ev->handle);
    return;
  }
}

/*
 *  Port event handler
 */
static void handle_port_event(const uevent_t * ev) {
  uuid_t peer_uuid;

  if ((ev->event & IPC_HANDLE_POLL_ERROR) ||
    (ev->event & IPC_HANDLE_POLL_HUP) ||
    (ev->event & IPC_HANDLE_POLL_MSG) ||
    (ev->event & IPC_HANDLE_POLL_SEND_UNBLOCKED)) {
    /* should never happen with port handles */
    TLOGE("error event (0x%x) for port (%d)\n",
      ev->event, ev->handle);
    abort();
  }
  if (ev->event & IPC_HANDLE_POLL_READY) {
    /* incoming connection: accept it */
    int rc = accept(ev->handle, &peer_uuid);
    if (rc < 0) {
      TLOGE("failed (%d) to accept on port %d\n",
        rc, ev->handle);
      return;
    }
    handle_t chan = rc;
    while (true){
      struct uevent cev;

      rc = wait(chan, &cev, INFINITE_TIME);
      if (rc < 0) {
        TLOGE("wait returned (%d)\n", rc);
        abort();
      }
      handle_channel_event(&cev);
      if (cev.event & IPC_HANDLE_POLL_HUP) {
        return;
      }
    }
  }
}


/*
 *  Main application entry point
 */
int main(void) {
  int rc;
  handle_t port;

  /* Initialize service */
  rc = port_create(srv_name, 1, MAX_ECHO_MSG_SIZE,
    IPC_PORT_ALLOW_NS_CONNECT |
    IPC_PORT_ALLOW_TA_CONNECT);
  if (rc < 0) {
    TLOGE("Failed (%d) to create port %s\n",
      rc, srv_name);
    abort();
  }
  port = (handle_t) rc;

  /* enter main event loop */
  while (true) {
    uevent_t ev;

    ev.handle = INVALID_IPC_HANDLE;
    ev.event = 0;
    ev.cookie = NULL;

    /* wait forever */
    rc = wait(port, &ev, INFINITE_TIME);
    if (rc == NO_ERROR) {
      /* got an event */
      handle_port_event(&ev);
    } else {
      TLOGE("wait returned (%d)\n", rc);
      abort();
    }
  }
  return 0;
}

Metode run_end_to_end_msg_test() mengirimkan 10.000 pesan secara asinkron ke layanan "echo" dan menangani balasan.

static int run_echo_test(void)
{
  int rc;
  handle_t chan;
  uevent_t uevt;
  uint8_t tx_buf[64];
  uint8_t rx_buf[64];
  ipc_msg_info_t inf;
  ipc_msg_t   tx_msg;
  iovec_t     tx_iov;
  ipc_msg_t   rx_msg;
  iovec_t     rx_iov;

  /* prepare tx message buffer */
  tx_iov.base = tx_buf;
  tx_iov.len  = sizeof(tx_buf);
  tx_msg.num_iov = 1;
  tx_msg.iov     = &tx_iov;
  tx_msg.num_handles = 0;
  tx_msg.handles = NULL;

  memset (tx_buf, 0x55, sizeof(tx_buf));

  /* prepare rx message buffer */
  rx_iov.base = rx_buf;
  rx_iov.len  = sizeof(rx_buf);
  rx_msg.num_iov = 1;
  rx_msg.iov     = &rx_iov;
  rx_msg.num_handles = 0;
  rx_msg.handles = NULL;

  /* open connection to echo service */
  rc = sync_connect(srv_name, 1000);
  if(rc < 0)
    return rc;

  /* got channel */
  chan = (handle_t)rc;

  /* send/receive 10000 messages asynchronously. */
  uint tx_cnt = 10000;
  uint rx_cnt = 10000;

  while (tx_cnt || rx_cnt) {
    /* send messages until all buffers are full */
while (tx_cnt) {
    rc = send_msg(chan, &tx_msg);
      if (rc == ERR_NOT_ENOUGH_BUFFER)
      break;  /* no more space */
    if (rc != 64) {
      if (rc > 0) {
        /* incomplete send */
        rc = ERR_NOT_VALID;
}
      goto abort_test;
}
    tx_cnt--;
  }

  /* wait for reply msg or room */
  rc = wait(chan, &uevt, 1000);
  if (rc != NO_ERROR)
    goto abort_test;

  /* drain all messages */
  while (rx_cnt) {
    /* get a reply */
      rc = get_msg(chan, &inf);
    if (rc == ERR_NO_MSG)
        break;  /* no more messages  */
  if (rc != NO_ERROR)
goto abort_test;

  /* read reply data */
    rc = read_msg(chan, inf.id, 0, &rx_msg);
  if (rc != 64) {
    /* unexpected reply length */
    rc = ERR_NOT_VALID;
    goto abort_test;
}

  /* discard reply */
  rc = put_msg(chan, inf.id);
  if (rc != NO_ERROR)
    goto abort_test;
  rx_cnt--;
  }
}

abort_test:
  close(chan);
  return rc;
}

API dan aplikasi dunia yang tidak aman

Serangkaian layanan Trusty, diterbitkan dari sisi aman dan ditandai dengan atribut IPC_PORT_ALLOW_NS_CONNECT , dapat diakses oleh program kernel dan ruang pengguna yang berjalan di sisi tidak aman.

Lingkungan eksekusi di sisi tidak aman (kernel dan ruang pengguna) sangat berbeda dengan lingkungan eksekusi di sisi aman. Oleh karena itu, dibandingkan satu perpustakaan untuk kedua lingkungan, ada dua set API yang berbeda. Di kernel, API Klien disediakan oleh driver kernel trusty-ipc dan mendaftarkan node perangkat karakter yang dapat digunakan oleh proses ruang pengguna untuk berkomunikasi dengan layanan yang berjalan di sisi aman.

Ruang pengguna API Klien IPC yang Terpercaya

Pustaka API Klien IPC Trusty ruang pengguna adalah lapisan tipis di atas node perangkat fd .

Program ruang pengguna memulai sesi komunikasi dengan memanggil tipc_connect() , menginisialisasi koneksi ke layanan Trusty yang ditentukan. Secara internal, panggilan tipc_connect() membuka node perangkat tertentu untuk mendapatkan deskriptor file dan memanggil panggilan TIPC_IOC_CONNECT ioctl() dengan parameter argp yang menunjuk ke string yang berisi nama layanan yang akan dihubungkan.

#define TIPC_IOC_MAGIC  'r'
#define TIPC_IOC_CONNECT  _IOW(TIPC_IOC_MAGIC, 0x80, char *)

Deskriptor file yang dihasilkan hanya dapat digunakan untuk berkomunikasi dengan layanan yang membuat file tersebut dibuat. Deskriptor file harus ditutup dengan memanggil tipc_close() ketika koneksi tidak diperlukan lagi.

Deskriptor file yang diperoleh dari panggilan tipc_connect() berperilaku sebagai node perangkat karakter tipikal; deskriptor file:

  • Dapat dialihkan ke mode non-pemblokiran jika diperlukan
  • Dapat ditulis menggunakan panggilan write() standar untuk mengirim pesan ke pihak lain
  • Dapat disurvei (menggunakan panggilan poll() atau panggilan select() ) untuk ketersediaan pesan masuk sebagai deskriptor file biasa
  • Dapat dibaca untuk mengambil pesan masuk

Penelepon mengirimkan pesan ke layanan Trusty dengan menjalankan panggilan tulis untuk fd yang ditentukan. Semua data yang diteruskan ke panggilan write() di atas diubah menjadi pesan oleh driver IPC yang terpercaya. Pesan dikirim ke sisi aman di mana data ditangani oleh subsistem IPC di kernel Trusty dan dirutekan ke tujuan yang tepat dan dikirim ke loop peristiwa aplikasi sebagai peristiwa IPC_HANDLE_POLL_MSG pada pegangan saluran tertentu. Tergantung pada protokol khusus layanan tertentu, layanan Trusty dapat mengirim satu atau lebih pesan balasan yang dikirim kembali ke sisi tidak aman dan ditempatkan di antrian pesan deskriptor file saluran yang sesuai untuk diambil oleh aplikasi ruang pengguna read() panggilan.

tipc_connect()

Membuka node perangkat tipc tertentu dan memulai koneksi ke layanan Trusty tertentu.

int tipc_connect(const char *dev_name, const char *srv_name);

[dalam] dev_name : Jalur ke node perangkat IPC Terpercaya yang akan dibuka

[dalam] srv_name : Nama layanan Terpercaya yang diterbitkan untuk dihubungkan

[retval]: Deskriptor file valid jika berhasil, -1 jika tidak.

tipc_close()

Menutup koneksi ke layanan Trusty yang ditentukan oleh deskriptor file.

int tipc_close(int fd);

[in] fd : Deskriptor file yang sebelumnya dibuka oleh panggilan tipc_connect()

API Klien IPC Kernel Terpercaya

Kernel Trusty IPC Client API tersedia untuk driver kernel. Ruang pengguna Trusty IPC API diimplementasikan di atas API ini.

Secara umum, penggunaan umum API ini terdiri dari pemanggil yang membuat objek struct tipc_chan dengan menggunakan fungsi tipc_create_channel() dan kemudian menggunakan panggilan tipc_chan_connect() untuk memulai koneksi ke layanan Trusty IPC yang berjalan di sisi aman. Koneksi ke sisi jarak jauh dapat diakhiri dengan memanggil tipc_chan_shutdown() diikuti dengan tipc_chan_destroy() untuk membersihkan sumber daya.

Setelah menerima pemberitahuan (melalui panggilan balik handle_event() ) bahwa koneksi telah berhasil dibuat, pemanggil melakukan hal berikut:

  • Mendapatkan buffer pesan menggunakan panggilan tipc_chan_get_txbuf_timeout()
  • Menulis pesan, dan
  • Mengantrekan pesan menggunakan metode tipc_chan_queue_msg() untuk dikirim ke layanan Trusty (di sisi aman), yang terhubung dengan saluran tersebut

Setelah antrian berhasil, pemanggil harus melupakan buffer pesan karena buffer pesan pada akhirnya kembali ke kumpulan buffer gratis setelah diproses oleh pihak jarak jauh (untuk digunakan kembali nanti, untuk pesan lain). Pengguna hanya perlu memanggil tipc_chan_put_txbuf() jika gagal mengantri buffer tersebut atau tidak diperlukan lagi.

Pengguna API menerima pesan dari sisi jarak jauh dengan menangani panggilan balik notifikasi handle_msg() (yang dipanggil dalam konteks antrian kerja rx terpercaya-ipc) yang menyediakan penunjuk ke buffer rx yang berisi pesan masuk untuk ditangani.

Implementasi callback handle_msg() diharapkan akan mengembalikan pointer ke struct tipc_msg_buf yang valid. Ini bisa sama dengan buffer pesan masuk jika ditangani secara lokal dan tidak diperlukan lagi. Atau, ini bisa berupa buffer baru yang diperoleh melalui panggilan tipc_chan_get_rxbuf() jika buffer yang masuk dimasukkan dalam antrean untuk diproses lebih lanjut. Buffer rx yang terlepas harus dilacak dan akhirnya dilepaskan menggunakan panggilan tipc_chan_put_rxbuf() ketika tidak lagi diperlukan.

Metode dalam Kernel Trusty IPC Client API

tipc_create_channel()

Membuat dan mengonfigurasi instance saluran IPC Terpercaya untuk perangkat IPC Terpercaya tertentu.

struct tipc_chan *tipc_create_channel(struct device *dev,
                          const struct tipc_chan_ops *ops,
                              void *cb_arg);

[in] dev : Penunjuk ke IPC terpercaya tempat saluran perangkat dibuat

[in] ops : Penunjuk ke struct tipc_chan_ops , dengan callback khusus pemanggil terisi

[di] cb_arg : Penunjuk ke data yang akan diteruskan ke panggilan balik tipc_chan_ops

[retval]: Penunjuk ke instance struct tipc_chan yang baru dibuat jika berhasil, ERR_PTR(err) jika tidak

Secara umum, pemanggil harus menyediakan dua callback yang dipanggil secara asinkron ketika aktivitas terkait terjadi.

Acara void (*handle_event)(void *cb_arg, int event) dipanggil untuk memberi tahu pemanggil tentang perubahan status saluran.

[di] cb_arg : Penunjuk ke data yang diteruskan ke panggilan tipc_create_channel()

[in] event : Suatu peristiwa yang dapat berupa salah satu dari nilai berikut:

  • TIPC_CHANNEL_CONNECTED - menunjukkan koneksi yang berhasil ke sisi jarak jauh
  • TIPC_CHANNEL_DISCONNECTED - menunjukkan sisi jarak jauh menolak permintaan koneksi baru atau meminta pemutusan sambungan untuk saluran yang terhubung sebelumnya
  • TIPC_CHANNEL_SHUTDOWN - menunjukkan sisi jarak jauh dimatikan, mengakhiri semua koneksi secara permanen

Callback struct tipc_msg_buf *(*handle_msg)(void *cb_arg, struct tipc_msg_buf *mb) dipanggil untuk memberikan pemberitahuan bahwa pesan baru telah diterima melalui saluran tertentu:

  • [di] cb_arg : Penunjuk ke data yang diteruskan ke panggilan tipc_create_channel()
  • [in] mb : Penunjuk ke struct tipc_msg_buf yang menjelaskan pesan masuk
  • [retval]: Implementasi panggilan balik diharapkan mengembalikan pointer ke struct tipc_msg_buf yang bisa menjadi pointer yang sama yang diterima sebagai parameter mb jika pesan ditangani secara lokal dan tidak diperlukan lagi (atau bisa berupa buffer baru yang diperoleh oleh tipc_chan_get_rxbuf() panggilan)

tipc_chan_connect()

Memulai koneksi ke layanan IPC Terpercaya yang ditentukan.

int tipc_chan_connect(struct tipc_chan *chan, const char *port);

[in] chan : Penunjuk ke saluran yang dikembalikan oleh panggilan tipc_create_chan()

[di] port : Penunjuk ke string yang berisi nama layanan yang akan dihubungkan

[retval]: 0 jika berhasil, sebaliknya kesalahan negatif

Penelepon diberitahu ketika koneksi dibuat dengan menerima panggilan balik handle_event .

tipc_chan_shutdown()

Mengakhiri koneksi ke layanan IPC Terpercaya yang sebelumnya dimulai oleh panggilan tipc_chan_connect() .

int tipc_chan_shutdown(struct tipc_chan *chan);

[in] chan : Penunjuk ke saluran yang dikembalikan oleh panggilan tipc_create_chan()

tipc_chan_destroy()

Menghancurkan saluran IPC Terpercaya tertentu.

void tipc_chan_destroy(struct tipc_chan *chan);

[in] chan : Penunjuk ke saluran yang dikembalikan oleh panggilan tipc_create_chan()

tipc_chan_get_txbuf_timeout()

Mendapatkan buffer pesan yang dapat digunakan untuk mengirim data melalui saluran tertentu. Jika buffer tidak segera tersedia, pemanggil mungkin diblokir selama batas waktu yang ditentukan (dalam milidetik).

struct tipc_msg_buf *
tipc_chan_get_txbuf_timeout(struct tipc_chan *chan, long timeout);

[in] chan : Penunjuk ke saluran tujuan antrian pesan

[in] chan : Batas waktu maksimum untuk menunggu hingga buffer tx tersedia

[retval]: Buffer pesan yang valid jika berhasil, ERR_PTR(err) jika terjadi kesalahan

tipc_chan_queue_msg()

Mengantri pesan untuk dikirim melalui saluran IPC Terpercaya yang ditentukan.

int tipc_chan_queue_msg(struct tipc_chan *chan, struct tipc_msg_buf *mb);

[in] chan : Penunjuk ke saluran tujuan antrian pesan

[dalam] mb: Penunjuk ke pesan yang akan diantri (diperoleh melalui panggilan tipc_chan_get_txbuf_timeout() )

[retval]: 0 jika berhasil, sebaliknya kesalahan negatif

tipc_chan_put_txbuf()

Melepaskan buffer pesan Tx tertentu yang sebelumnya diperoleh melalui panggilan tipc_chan_get_txbuf_timeout() .

void tipc_chan_put_txbuf(struct tipc_chan *chan,
                         struct tipc_msg_buf *mb);

[in] chan : Penunjuk ke saluran tempat buffer pesan ini berada

[in] mb : Penunjuk ke buffer pesan yang akan dirilis

[pengembalian]: Tidak ada

tipc_chan_get_rxbuf()

Mendapatkan buffer pesan baru yang dapat digunakan untuk menerima pesan melalui saluran yang ditentukan.

struct tipc_msg_buf *tipc_chan_get_rxbuf(struct tipc_chan *chan);

[in] chan : Penunjuk ke saluran tempat buffer pesan ini berada

[retval]: Buffer pesan yang valid jika berhasil, ERR_PTR(err) jika terjadi kesalahan

tipc_chan_put_rxbuf()

Melepaskan buffer pesan tertentu yang sebelumnya diperoleh melalui panggilan tipc_chan_get_rxbuf() .

void tipc_chan_put_rxbuf(struct tipc_chan *chan,
                         struct tipc_msg_buf *mb);

[in] chan : Penunjuk ke saluran tempat buffer pesan ini berada

[in] mb : Penunjuk ke buffer pesan yang akan dirilis

[pengembalian]: Tidak ada