Referensi API Tepercaya

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 API Tepercaya untuk terhubung ke aplikasi/layanan tepercaya dan bertukar pesan sewenang-wenang dengan mereka seperti layanan jaringan melalui IP. Terserah aplikasi untuk menentukan format data dan semantik pesan ini menggunakan protokol tingkat aplikasi. Pengiriman pesan yang andal dijamin oleh infrastruktur Trusty yang mendasarinya (dalam bentuk driver yang berjalan pada prosesor utama), dan komunikasi benar-benar asinkron.

Pelabuhan dan saluran

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

Ketika klien terhubung ke port, klien menerima saluran untuk berinteraksi dengan layanan. Layanan harus menerima koneksi masuk, dan ketika itu diterima, layanan juga menerima saluran. Intinya, port digunakan untuk mencari layanan dan kemudian komunikasi terjadi melalui sepasang saluran yang terhubung (yaitu, instance 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 salah satu pihak memutuskan untuk memutuskan koneksi.

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

Bergantung pada persyaratan, aplikasi tepercaya dapat menjadi klien dan server pada saat yang bersamaan. Aplikasi tepercaya yang menerbitkan layanan (sebagai server) mungkin perlu terhubung ke layanan lain (sebagai klien).

Menangani API

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

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

Metode di Handle API

Pegangan hanya valid dalam konteks aplikasi. Aplikasi tidak boleh meneruskan nilai pegangan ke aplikasi lain kecuali ditentukan secara eksplisit. Nilai pegangan saja harus ditafsirkan 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 yang ditentukan.

long set_cookie(uint32_t handle, void *cookie)

[in] handle : Setiap handle yang dikembalikan oleh salah satu panggilan API

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

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

Panggilan ini berguna untuk menangani peristiwa ketika terjadi di lain waktu setelah pegangan dibuat. Mekanisme penanganan peristiwa memasok pegangan dan cookie-nya kembali ke pengendali peristiwa.

Pegangan dapat ditunggu untuk acara dengan menggunakan panggilan wait() .

tunggu()

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

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

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

[out] event : Sebuah pointer ke struktur yang mewakili sebuah event yang terjadi pada handle ini

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

[retval]: NO_ERROR jika peristiwa yang valid terjadi dalam interval waktu habis yang ditentukan; ERR_TIMED_OUT jika batas waktu yang ditentukan telah berlalu tetapi 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 benar-benar tertunda, penelepon harus memulai ulang penantian

IPC_HANDLE_POLL_ERROR - telah terjadi kesalahan internal yang tidak ditentukan

IPC_HANDLE_POLL_READY - tergantung pada jenis pegangan, sebagai berikut:

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

Peristiwa berikut hanya relevan untuk saluran:

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

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

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

Handle 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);

[in] handle_id : Menangani 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 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 berjalan di kedua arah, jadi menentukan 1 di sini berarti 1 buffer kirim dan 1 buffer penerima telah dialokasikan sebelumnya. Secara umum, jumlah buffer yang diperlukan tergantung pada kesepakatan protokol tingkat yang lebih tinggi antara klien dan server. Jumlahnya bisa sedikitnya 1 untuk protokol yang sangat sinkron (kirim pesan, terima balasan sebelum mengirim yang lain). Tetapi jumlahnya bisa lebih jika klien mengharapkan untuk mengirim lebih dari satu pesan sebelum balasan dapat muncul (misalnya, satu pesan sebagai prolog dan satu lagi sebagai perintah sebenarnya). Set buffer yang dialokasikan adalah per saluran, jadi dua koneksi (saluran) terpisah akan memiliki set buffer terpisah.

[in] recv_buf_size : Ukuran maksimum dari masing-masing 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 spesifik jika negatif

Server kemudian melakukan polling daftar pegangan port untuk koneksi masuk menggunakan panggilan wait() . Setelah menerima permintaan koneksi yang ditunjukkan oleh bit IPC_HANDLE_POLL_READY yang disetel 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);

[in] handle_id : Pegangan yang mewakili port yang terhubung dengan klien

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

[retval]: Menangani saluran (jika non-negatif) tempat 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 oleh nama.

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

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

[in] flags : Menentukan tambahan, perilaku opsional

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

Jika tidak ada flags yang ditentukan (parameter flags disetel ke 0), panggilan connect() 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, yang dijelaskan di bawah ini:

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, alih-alih langsung gagal.

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

API Perpesanan

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 ke saluran dengan mengeluarkan panggilan connect() , dan server mendapatkan pegangan saluran dari panggilan accept() , dijelaskan di atas.

Struktur pesan Terpercaya

Seperti yang ditunjukkan berikut ini, pesan yang dipertukarkan oleh Trusty API memiliki struktur minimal, menyerahkannya ke 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 lebih buffer yang tidak bersebelahan yang diwakili oleh larik 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 benar-benar arbitrer.

Metode di Messaging API

kirim_pesan()

Mengirim pesan melalui saluran tertentu.

long send_msg(uint32_t handle, ipc_msg_t *msg);

[in] handle : Menangani saluran untuk mengirim pesan

[in] msg : Pointer ke ipc_msg_t structure menjelaskan pesan

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

Jika klien (atau server) mencoba mengirim pesan melalui saluran dan tidak ada ruang dalam antrian pesan rekan tujuan, saluran mungkin memasuki status diblokir-kirim (ini seharusnya tidak pernah 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 itu, pemanggil harus menunggu sampai rekan mengosongkan beberapa ruang dalam antrian penerimaannya dengan mengambil pesan penanganan dan penghentian, yang ditunjukkan oleh bit IPC_HANDLE_POLL_SEND_UNBLOCKED yang disetel di bidang event struktur uevent_t yang dikembalikan oleh panggilan wait() .

get_msg()

Mendapat meta-informasi tentang pesan berikutnya dalam antrian pesan masuk

dari saluran tertentu.

long get_msg(uint32_t handle, ipc_msg_info_t *msg_info);

[in] handle : Menangani saluran tempat pesan baru harus diambil

[out] msg_info : Struktur informasi pesan digambarkan 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 beredar, dan panjang total setiap pesan diisi. Jika dikonfigurasi dan diizinkan oleh protokol, mungkin ada beberapa pesan yang beredar (dibuka) sekaligus untuk saluran tertentu.

[retval]: NO_ERROR pada keberhasilan; kesalahan negatif sebaliknya

baca_pesan()

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 : Menangani saluran untuk membaca pesan

[in] msg_id : ID pesan yang akan dibaca

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

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

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

Metode read_msg dapat dipanggil beberapa kali mulai dari 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 : Menangani saluran tempat pesan telah tiba

[in] msg_id : ID pesan dihentikan

[retval]: NO_ERROR pada keberhasilan; kesalahan negatif sebaliknya

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

API Pendeskripsi File

File Descriptor API menyertakan panggilan read() , write() , dan ioctl() . Semua panggilan ini dapat beroperasi pada set deskriptor file yang telah ditentukan (statis) yang secara tradisional 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 (standar dan terkenal):

  • 0 - masukan standar. Implementasi default dari input standar fd adalah tanpa operasi (karena aplikasi tepercaya tidak diharapkan memiliki konsol interaktif) jadi membaca, menulis, atau menjalankan ioctl() pada fd 0 akan mengembalikan kesalahan ERR_NOT_SUPPORTED .
  • 1 - keluaran standar. Data yang ditulis ke output standar dapat dirutekan (bergantung pada tingkat debug LK) ke UART dan/atau log memori yang tersedia di sisi yang tidak aman, bergantung pada platform dan konfigurasi. Log dan pesan debug yang tidak penting harus masuk dalam keluaran standar. Metode read() dan ioctl() adalah no-ops dan harus 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 yang tidak aman, bergantung pada platform dan konfigurasi. Disarankan untuk menulis hanya pesan kritis ke kesalahan standar, karena aliran ini sangat mungkin tidak dibatasi. Metode read() dan ioctl() adalah no-ops dan harus mengembalikan kesalahan ERR_NOT_SUPPORTED .

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

Metode di File Descriptor API

Baca()

Mencoba membaca hingga count byte data dari deskriptor file tertentu.

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

[in] fd : Deskriptor file untuk dibaca

[out] buf : Pointer ke buffer tempat menyimpan data

[in] 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);

[in] fd : Deskriptor file yang akan ditulis

[out] buf : Pointer ke data yang akan ditulis

[in] count : Jumlah byte maksimum untuk ditulis

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

ioctl()

Memanggil perintah ioctl yang ditentukan untuk deskriptor file yang diberikan.

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

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

[dalam] cmd : Perintah ioctl

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

API lain-lain

Metode di Miscellaneous API

dapatkan waktu()

Mengembalikan waktu sistem saat ini (dalam nanodetik).

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

[in] clock_id : Tergantung platform; lulus nol untuk default

[di] flags : Dicadangkan, harus nol

[in] time : Pointer ke nilai int64_t untuk menyimpan waktu saat ini

[retval]: NO_ERROR pada keberhasilan; kesalahan negatif sebaliknya

tidur nano()

Menangguhkan eksekusi aplikasi panggilan untuk jangka waktu tertentu dan melanjutkannya setelah periode itu.

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

[in] clock_id : Dipesan, harus nol

[di] flags : Dicadangkan, harus nol

[dalam] sleep_time : Waktu tidur dalam nanodetik

[retval]: NO_ERROR pada keberhasilan; kesalahan negatif sebaliknya

Contoh server aplikasi tepercaya

Contoh aplikasi berikut menunjukkan penggunaan API di atas. Sampel membuat layanan "gema" yang menangani beberapa koneksi masuk dan mencerminkan kembali ke pemanggil semua pesan yang diterimanya 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

Satu set layanan Terpercaya, diterbitkan 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 (kernel dan ruang pengguna) sangat berbeda dari lingkungan eksekusi di sisi aman. Oleh karena itu, daripada perpustakaan tunggal untuk kedua lingkungan, ada dua set API yang berbeda. Di kernel, API Klien disediakan oleh driver kernel ipc terpercaya 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 Tepercaya

Ruang pengguna Pustaka API Klien IPC Trusty 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() membuka node perangkat tertentu untuk mendapatkan deskriptor file dan memanggil TIPC_IOC_CONNECT ioctl() dengan parameter argp 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 dibuatnya. Deskriptor file harus ditutup dengan memanggil tipc_close() saat koneksi tidak diperlukan lagi.

Deskriptor file yang diperoleh oleh panggilan tipc_connect() berperilaku sebagai simpul perangkat karakter khas; deskriptor file:

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

Penelepon 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 driver trusty-ipc. Pesan dikirim ke sisi aman tempat data ditangani oleh subsistem IPC di kernel Trusty dan dirutekan ke tujuan yang tepat dan dikirimkan ke loop peristiwa aplikasi sebagai peristiwa IPC_HANDLE_POLL_MSG pada pegangan saluran tertentu. Bergantung pada protokol khusus layanan tertentu, layanan Trusty dapat mengirim satu atau lebih pesan balasan yang dikirimkan kembali ke sisi yang tidak aman dan ditempatkan di antrian pesan deskriptor file saluran yang sesuai untuk diambil oleh aplikasi ruang pengguna read() panggilan.

tipc_koneksi()

Membuka node perangkat tipc tertentu 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 IPC Tepercaya untuk dibuka

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

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

tipc_close()

Menutup koneksi ke layanan Trusty yang ditentukan oleh deskriptor file.

int tipc_close(int fd);

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

API Klien IPC Tepercaya Kernel

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

Secara umum, penggunaan khas 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. Sambungan ke sisi jarak jauh dapat diakhiri dengan memanggil tipc_chan_shutdown() diikuti oleh tipc_chan_destroy() untuk membersihkan sumber daya.

Setelah menerima pemberitahuan (melalui callback 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 pengiriman ke layanan Trusty (di sisi aman), yang terhubung dengan saluran

Setelah antrian berhasil, pemanggil harus melupakan buffer pesan karena buffer pesan akhirnya kembali ke kumpulan buffer gratis setelah diproses oleh sisi 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 antrean kerja rx trusty-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. Itu bisa sama dengan buffer pesan masuk jika ditangani secara lokal dan tidak diperlukan lagi. Atau, itu bisa berupa buffer baru yang diperoleh dengan panggilan tipc_chan_get_rxbuf() jika buffer yang masuk diantrekan untuk diproses lebih lanjut. Buffer rx yang terpisah harus dilacak dan akhirnya dirilis menggunakan panggilan tipc_chan_put_rxbuf() saat tidak lagi diperlukan.

Metode di Kernel Trusty IPC Client API

tipc_create_channel()

Membuat dan mengonfigurasi instance saluran 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 : Pointer ke ipc terpercaya yang saluran perangkatnya dibuat

[in] ops : Pointer ke struct tipc_chan_ops , dengan callback khusus penelepon terisi

[in] cb_arg : Pointer ke data yang akan diteruskan ke callback tipc_chan_ops

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

Secara umum, pemanggil harus menyediakan dua panggilan balik yang dipanggil secara tidak sinkron ketika aktivitas terkait sedang terjadi.

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

[in] cb_arg : Pointer ke data yang diteruskan ke panggilan tipc_create_channel()

[in] event : Suatu event 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 untuk saluran yang terhubung sebelumnya
  • TIPC_CHANNEL_SHUTDOWN - menunjukkan sisi jarak jauh dimatikan, secara permanen mengakhiri semua koneksi

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:

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

tipc_chan_connect()

Memulai koneksi ke layanan Trusty IPC yang ditentukan.

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

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

[in] port : Pointer ke string yang berisi nama layanan yang akan dihubungkan

[retval]: 0 pada keberhasilan, kesalahan negatif sebaliknya

Penelepon diberi tahu saat koneksi dibuat dengan menerima panggilan balik handle_event .

tipc_chan_shutdown()

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

int tipc_chan_shutdown(struct tipc_chan *chan);

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

tipc_chan_destroy()

Menghancurkan saluran Trusty IPC tertentu.

void tipc_chan_destroy(struct tipc_chan *chan);

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

tipc_chan_get_txbuf_timeout()

Memperoleh 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 : Pointer ke saluran yang akan digunakan untuk mengantri pesan

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

[retval]: Buffer pesan yang valid saat berhasil, ERR_PTR(err) saat error

tipc_chan_queue_msg()

Antrean pesan yang akan dikirim melalui saluran Trusty IPC yang ditentukan.

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

[in] chan : Pointer ke saluran yang akan digunakan untuk mengantri pesan

[in] mb: Pointer ke pesan ke antrian (diperoleh dengan panggilan tipc_chan_get_txbuf_timeout() )

[retval]: 0 pada keberhasilan, kesalahan negatif sebaliknya

tipc_chan_put_txbuf()

Melepaskan buffer pesan Tx yang ditentukan sebelumnya diperoleh dengan panggilan tipc_chan_get_txbuf_timeout() .

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

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

[in] mb : Pointer ke buffer pesan untuk melepaskan

[retval]: Tidak ada

tipc_chan_get_rxbuf()

Memperoleh 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 : Pointer ke saluran tempat buffer pesan ini berada

[retval]: Buffer pesan yang valid saat berhasil, ERR_PTR(err) saat error

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 : Pointer ke saluran tempat buffer pesan ini berada

[in] mb : Pointer ke buffer pesan untuk dilepaskan

[retval]: Tidak ada