Güvenilir API Referansı

Trusty, iki sınıf uygulama/hizmet geliştirmek için API'ler sağlar:

  • TEE işlemcisinde çalışan güvenilir uygulamalar veya hizmetler
  • Ana işlemcide çalışan ve güvenilir uygulamalar tarafından sağlanan hizmetleri kullanan normal/güvenilmeyen uygulamalar

Trusty API, genellikle güvenli olmayan dünyayla iletişimler dahil olmak üzere Trusty süreçler arası iletişim (IPC) sistemini tanımlar. Ana işlemci üzerinde çalışan yazılım, tıpkı IP üzerinden bir ağ hizmeti gibi, güvenilir uygulamalara/hizmetlere bağlanmak ve bunlarla rastgele mesaj alışverişi yapmak için Trusty API'lerini kullanabilir. Uygulama düzeyinde bir protokol kullanarak bu mesajların veri biçimini ve anlamını belirlemek uygulamaya bağlıdır. İletilerin güvenilir bir şekilde teslimi, temeldeki Trusty altyapısı (ana işlemci üzerinde çalışan sürücüler biçiminde) tarafından garanti edilir ve iletişim tamamen eşzamansızdır.

Bağlantı noktaları ve kanallar

Bağlantı noktaları, istemcilerin bağlandığı adlandırılmış bir yol biçiminde hizmet uç noktalarını ortaya çıkarmak için Trusty uygulamaları tarafından kullanılır. Bu, istemcilerin kullanması için basit, dize tabanlı bir hizmet kimliği verir. Adlandırma kuralı, ters DNS stili adlandırmadır, örneğin com.google.servicename .

Bir istemci bir bağlantı noktasına bağlandığında, istemci bir hizmetle etkileşim kurmak için bir kanal alır. Servis gelen bir bağlantıyı kabul etmelidir ve kabul ettiğinde de bir kanal alır. Özünde, bağlantı noktaları hizmetleri aramak için kullanılır ve ardından iletişim bir çift bağlı kanal üzerinden gerçekleşir (yani, bir bağlantı noktasındaki bağlantı örnekleri). İstemci bir bağlantı noktasına bağlandığında simetrik, çift yönlü bir bağlantı kurulur. Bu tam çift yönlü yolu kullanarak, istemciler ve sunucular, her iki taraf da bağlantıyı kesmeye karar verene kadar rastgele mesaj alışverişi yapabilir.

Yalnızca güvenli taraftaki güvenilir uygulamalar veya Güvenilir çekirdek modülleri bağlantı noktaları oluşturabilir. Güvenli olmayan tarafta (normal dünyada) çalışan uygulamalar yalnızca güvenli taraf tarafından yayınlanan servislere bağlanabilir.

Gereksinimlere bağlı olarak, güvenilir bir uygulama aynı anda hem istemci hem de sunucu olabilir. Bir hizmeti (sunucu olarak) yayınlayan güvenilir bir uygulamanın diğer hizmetlere (istemci olarak) bağlanması gerekebilir.

API'yi işle

Tutamaçlar, UNIX'teki dosya tanımlayıcılara benzer şekilde, bağlantı noktaları ve kanallar gibi kaynakları temsil eden işaretsiz tam sayılardır. Tutamaçlar oluşturulduktan sonra, uygulamaya özel bir tutamaç tablosuna yerleştirilirler ve daha sonra başvurulabilirler.

Bir arayan, set_cookie() yöntemini kullanarak özel verileri bir tanıtıcıyla ilişkilendirebilir.

Handle API'sindeki Yöntemler

Tutamaçlar yalnızca bir uygulama bağlamında geçerlidir. Bir uygulama, açıkça belirtilmedikçe bir tanıtıcının değerini diğer uygulamalara iletmemelidir. Bir tanıtıcı değeri yalnızca INVALID_IPC_HANDLE #define, ile karşılaştırılarak yorumlanmalıdır.

Arayanın sağladığı özel verileri belirtilen bir tanıtıcıyla ilişkilendirir.

long set_cookie(uint32_t handle, void *cookie)

[içinde] handle : API çağrılarından biri tarafından döndürülen herhangi bir tanıtıcı

[içinde] cookie : Trusty uygulamasında rastgele kullanıcı alanı verilerine işaretçi

[retval]: NO_ERROR , aksi takdirde < 0 hata kodu

Bu çağrı, tanıtıcı oluşturulduktan sonra daha sonra meydana geldiklerinde olayları işlemek için kullanışlıdır. Olay işleme mekanizması, tanıtıcıyı ve tanımlama bilgisini olay işleyicisine geri sağlar.

wait() çağrısı kullanılarak olaylar için tanıtıcılar beklenebilir.

Bekle()

Belirli bir süre boyunca belirli bir tanıtıcıda bir olayın gerçekleşmesini bekler.

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

[içinde] handle_id : API çağrılarından biri tarafından döndürülen herhangi bir tanıtıcı

[out] event : Bu tanıtıcıda meydana gelen bir olayı temsil eden yapıya işaretçi

[in] timeout_msecs : Milisaniye cinsinden bir zaman aşımı değeri; -1 değeri sonsuz bir zaman aşımıdır

[retval]: NO_ERROR , belirtilen bir zaman aşımı aralığında geçerli bir olay meydana geldiyse; ERR_TIMED_OUT , belirtilen bir zaman aşımı süresi geçmişse ancak herhangi bir olay meydana gelmemişse; < 0 diğer hatalar için

Başarı üzerine ( retval == NO_ERROR ), wait() çağrısı belirtilen bir uevent_t yapısını meydana gelen olayla ilgili bilgilerle doldurur.

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;

event alanı, aşağıdaki değerlerin bir kombinasyonunu içerir:

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 - gerçekte bekleyen hiçbir olay yok, arayan beklemeyi yeniden başlatmalı

IPC_HANDLE_POLL_ERROR - belirtilmemiş bir dahili hata oluştu

IPC_HANDLE_POLL_READY - aşağıdaki gibi tanıtıcı türüne bağlıdır:

  • Bağlantı noktaları için bu değer, bekleyen bir bağlantı olduğunu gösterir.
  • Kanallar için bu değer, asenkron bir bağlantının kurulduğunu gösterir (bkz. connect() )

Aşağıdaki olaylar yalnızca kanallar için geçerlidir:

  • IPC_HANDLE_POLL_HUP - bir kanalın bir eş tarafından kapatıldığını gösterir
  • IPC_HANDLE_POLL_MSG - bu kanal için bekleyen bir mesaj olduğunu gösterir
  • IPC_HANDLE_POLL_SEND_UNBLOCKED - önceden gönderilmesi engellenen bir arayanın tekrar mesaj göndermeyi deneyebileceğini belirtir (ayrıntılar için send_msg() açıklamasına bakın)

Aynı anda birden çok bit ayarlanabileceğinden, belirtilen olayların bir kombinasyonunu işlemek için bir olay işleyici hazırlanmalıdır. Örneğin, bir kanal için bekleyen mesajlara ve aynı anda bir eş tarafından kapatılan bir bağlantıya sahip olmak mümkündür.

Çoğu olay yapışkandır. Temel koşul devam ettiği sürece devam ederler (örneğin, bekleyen tüm iletiler alınır ve bekleyen bağlantı istekleri işlenir). Bunun istisnası, okunduğunda temizlenen ve uygulamanın bunu işlemek için yalnızca bir şansı olan IPC_HANDLE_POLL_SEND_UNBLOCKED olayıdır.

Close close() yöntemi çağrılarak tanıtıcılar yok edilebilir.

kapat()

Belirtilen tanıtıcıyla ilişkili kaynağı yok eder ve onu tanıtıcı tablosundan kaldırır.

long close(uint32_t handle_id);

[içinde] handle_id : Yok etmek için tut

[geri dönüş]: başarılı ise 0; aksi halde olumsuz bir hata

Sunucu API'si

Bir sunucu, hizmet uç noktalarını temsil eden bir veya daha fazla adlandırılmış bağlantı noktası oluşturarak başlar. Her bağlantı noktası bir tanıtıcı ile temsil edilir.

Sunucu API'sindeki Yöntemler

port_create()

Adlandırılmış bir hizmet bağlantı noktası oluşturur.

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

[içinde] path : Bağlantı noktasının dize adı (yukarıda açıklandığı gibi). Bu ad, sistem genelinde benzersiz olmalıdır; bir kopya oluşturma girişimleri başarısız olur.

[içinde] num_recv_bufs : Bu bağlantı noktasındaki bir kanalın istemciyle veri alışverişini kolaylaştırmak için önceden ayırabileceği maksimum arabellek sayısı. Arabellekler, her iki yönde giden veriler için ayrı olarak sayılır, bu nedenle burada 1'in belirtilmesi, 1 gönderme ve 1 alma arabelleğinin önceden tahsis edildiği anlamına gelir. Genel olarak, gerekli arabellek sayısı, istemci ve sunucu arasındaki üst düzey protokol anlaşmasına bağlıdır. Çok senkronize bir protokol olması durumunda sayı 1 kadar küçük olabilir (mesaj gönder, başka bir göndermeden önce cevap al). Ancak, müşteri bir yanıt görünmeden önce birden fazla mesaj göndermeyi umuyorsa sayı daha fazla olabilir (örneğin, bir önsöz olarak bir mesaj ve asıl komut olarak başka bir mesaj). Ayrılan arabellek kümeleri kanal başınadır, bu nedenle iki ayrı bağlantının (kanallar) ayrı arabellek kümeleri olur.

[içinde] recv_buf_size : Yukarıdaki arabellek kümesindeki her bir arabelleğin maksimum boyutu. Bu değer protokole bağlıdır ve eş ile değiş tokuş edebileceğiniz maksimum mesaj boyutunu etkili bir şekilde sınırlar.

[içinde] flags : Ek bağlantı noktası davranışını belirten bir bayrak kombinasyonu

Bu değer, aşağıdaki değerlerin bir kombinasyonu olmalıdır:

IPC_PORT_ALLOW_TA_CONNECT - diğer güvenli uygulamalardan bağlantıya izin verir

IPC_PORT_ALLOW_NS_CONNECT - güvenli olmayan dünyadan bağlantıya izin verir

[retval]: Negatif değilse oluşturulan bağlantı noktasına veya negatifse belirli bir hataya işle

Sunucu daha sonra wait() çağrısını kullanarak gelen bağlantılar için bağlantı noktası tanıtıcılarının listesini yoklar. uevent_t yapısının event alanında ayarlanmış IPC_HANDLE_POLL_READY biti tarafından belirtilen bir bağlantı talebi alındığında, sunucu bir bağlantı kurmayı bitirmek ve daha sonra gelen mesajlar için sorgulanabilecek bir kanal (başka bir tanıtıcı tarafından temsil edilen) oluşturmak için accept() öğesini çağırmalıdır. .

kabul()

Gelen bir bağlantıyı kabul eder ve bir kanal için bir tanıtıcı alır.

long accept(uint32_t handle_id, uuid_t *peer_uuid);

[içinde] handle_id : Bir istemcinin bağlandığı bağlantı noktasını temsil eden tanıtıcı

[out] peer_uuid : Bağlanan istemci uygulamasının UUID'si ile doldurulacak bir uuud_t yapısının işaretçisi. Bağlantı güvenli olmayan bir dünyadan geliyorsa, tüm sıfırlara ayarlanacaktır.

[retval]: Sunucunun istemciyle mesaj alışverişinde bulunabileceği (veya aksi takdirde bir hata kodu) bir kanala (negatif değilse) işleyin

İstemci API'sı

Bu bölüm, İstemci API'sindeki yöntemleri içerir.

İstemci API'sindeki yöntemler

bağlamak()

Ada göre belirtilen bir bağlantı noktasına bağlantı başlatır.

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

[içinde] path : Güvenilir bir uygulama tarafından yayınlanan bir bağlantı noktasının adı

[içinde] flags : Ek, isteğe bağlı davranışı belirtir

[retval]: Sunucuyla mesaj alışverişinin yapılabileceği bir kanala yönlendirin; negatif ise hata

Hiçbir flags belirtilmezse ( flags parametresi 0 olarak ayarlanır), connect() öğesinin çağrılması, belirtilen bağlantı noktasına eşzamanlı bir bağlantı başlatır ve bu bağlantı noktası yoksa hemen bir hata verir ve sunucu aksi takdirde bir bağlantıyı kabul edene kadar bir blok oluşturur. .

Bu davranış, aşağıda açıklanan iki değerin bir kombinasyonunu belirterek değiştirilebilir:

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

IPC_CONNECT_WAIT_FOR_PORT - belirtilen bağlantı noktası yürütme sırasında hemen başarısız olmak yerine hemen mevcut değilse, connect() çağrısını beklemeye zorlar.

IPC_CONNECT_ASYNC - ayarlanırsa, eşzamansız bir bağlantı başlatır. Bir uygulamanın, normal çalışmaya başlamadan önce uevent_t yapısının olay alanında ayarlanmış IPC_HANDLE_POLL_READY biti tarafından belirtilen bir bağlantı tamamlama olayı için wait() işlevini çağırarak döndürülen tanıtıcıyı yoklaması gerekir.

Mesajlaşma API'sı

Messaging API çağrıları, önceden kurulmuş bir bağlantı (kanal) üzerinden mesajların gönderilmesini ve okunmasını sağlar. Messaging API çağrıları, sunucular ve istemciler için aynıdır.

Bir istemci, bir connect() çağrısı yayınlayarak bir kanal için bir tanıtıcı alır ve bir sunucu, yukarıda açıklanan bir accept() çağrısından bir kanal tanıtıcısı alır.

Güvenilir mesajın yapısı

Aşağıda gösterildiği gibi, Trusty API tarafından değiştirilen mesajlar minimum bir yapıya sahiptir ve gerçek içeriğin semantiği üzerinde anlaşmayı sunucu ve istemciye bırakır:

/*
 *  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;

Bir mesaj, bir dizi iovec_t yapısı tarafından temsil edilen bir veya daha fazla bitişik olmayan tampondan oluşabilir. Trusty, iov dizisini kullanarak dağılım toplama okumaları ve bu bloklara yazma işlemleri gerçekleştirir. iov dizisi tarafından tanımlanabilen arabelleklerin içeriği tamamen keyfidir.

Mesajlaşma API'sindeki Yöntemler

gönder_msg()

Belirli bir kanal üzerinden bir mesaj gönderir.

long send_msg(uint32_t handle, ipc_msg_t *msg);

[içinde] handle : İletinin gönderileceği kanalı işle

[içinde] msg : İletiyi açıklayan ipc_msg_t structure işaretçisi

[retval]: Başarıyla gönderilen toplam bayt sayısı; aksi halde olumsuz bir hata

İstemci (veya sunucu) kanal üzerinden bir mesaj göndermeye çalışıyorsa ve hedef eş mesaj kuyruğunda yer yoksa, kanal gönderme engellendi durumuna girebilir (bu, basit bir eşzamanlı istek/yanıt protokolü için asla olmamalıdır). ancak daha karmaşık durumlarda olabilir) bu, bir ERR_NOT_ENOUGH_BUFFER hata kodu döndürülerek belirtilir. Böyle bir durumda, arayan kişi, wait() çağrısı tarafından döndürülen uevent_t yapısının event alanında ayarlanmış IPC_HANDLE_POLL_SEND_UNBLOCKED biti tarafından belirtilen işleme ve kullanımdan kaldırma iletilerini alarak, eş, alma kuyruğunda bir miktar alan boşaltana kadar beklemelidir.

get_msg()

Gelen mesaj kuyruğundaki bir sonraki mesaj hakkında meta bilgi alır

belirli bir kanalın

long get_msg(uint32_t handle, ipc_msg_info_t *msg_info);

[içinde] handle : Yeni bir mesajın alınması gereken kanalın tanıtıcısı

[out] msg_info : Aşağıdaki gibi açıklanan mesaj bilgi yapısı:

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

Her mesaja, bekleyen mesajlar kümesi boyunca benzersiz bir kimlik atanır ve her mesajın toplam uzunluğu doldurulur. Protokol tarafından yapılandırılır ve izin verilirse, belirli bir kanal için aynı anda birden fazla bekleyen (açılmış) mesaj olabilir.

[retval]: NO_ERROR ; aksi halde olumsuz bir hata

read_msg()

Belirtilen uzaklıktan başlayarak belirtilen kimliğe sahip mesajın içeriğini okur.

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

[içinde] handle : Mesajın okunacağı kanalın tanıtıcısı

[içinde] msg_id : Okunacak mesajın kimliği

[içinde] offset : Okumaya başlanacak mesaja ofset

[içinde] msg : Gelen ileti verilerinin depolanacağı bir arabellek kümesini açıklayan ipc_msg_t yapısının işaretçisi

[retval]: Başarı durumunda dst arabelleklerinde depolanan toplam bayt sayısı; aksi halde olumsuz bir hata

read_msg yöntemi, gerektiğinde farklı (mutlaka sıralı değil) bir ofsetten başlayarak birden çok kez çağrılabilir.

put_msg()

Belirtilen kimliğe sahip bir iletiyi emekli eder.

long put_msg(uint32_t handle, uint32_t msg_id);

[içinde] handle : İletinin ulaştığı kanalın tanıtıcısı

[içinde] msg_id : Kullanımdan kaldırılan iletinin kimliği

[retval]: NO_ERROR ; aksi halde olumsuz bir hata

Bir mesaj kullanımdan kaldırıldıktan ve işgal ettiği arabellek serbest bırakıldıktan sonra mesaj içeriğine erişilemez.

Dosya Tanımlayıcı API'sı

Dosya Tanımlayıcı API'si read() , write() ve ioctl() çağrılarını içerir. Bu çağrıların tümü, geleneksel olarak küçük sayılarla temsil edilen önceden tanımlanmış (statik) bir dosya tanımlayıcı kümesi üzerinde çalışabilir. Geçerli uygulamada, dosya tanımlayıcı alanı IPC tanıtıcı alanından ayrıdır. Trusty'deki Dosya Tanımlayıcı API'si, geleneksel bir dosya tanımlayıcı tabanlı API'ye benzer.

Varsayılan olarak, önceden tanımlanmış 3 (standart ve iyi bilinen) dosya tanımlayıcı vardır:

  • 0 - standart giriş. Standart girdi fd varsayılan uygulaması işlemsizdir (güvenilir uygulamaların etkileşimli bir konsola sahip olması beklenmediğinden), bu nedenle fd 0'da ioctl() okuma, yazma veya çağırma işlemi bir ERR_NOT_SUPPORTED hatası döndürmelidir.
  • 1 - standart çıktı. Standart çıktıya yazılan veriler (LK hata ayıklama düzeyine bağlı olarak) UART'a ve/veya platforma ve yapılandırmaya bağlı olarak güvenli olmayan tarafta bulunan bir bellek günlüğüne yönlendirilebilir. Kritik olmayan hata ayıklama günlükleri ve mesajları standart çıktıya girmelidir. read() ve ioctl() yöntemleri işlem gerektirmez ve bir ERR_NOT_SUPPORTED hatası döndürmelidir.
  • 2 - standart hata. Standart hataya yazılan veriler, platforma ve yapılandırmaya bağlı olarak UART'a veya güvenli olmayan tarafta bulunan bellek günlüğüne yönlendirilmelidir. Bu akışın kısıtlanmama olasılığı çok yüksek olduğundan, yalnızca standart hataya kritik mesajların yazılması önerilir. read() ve ioctl() yöntemleri işlem gerektirmez ve bir ERR_NOT_SUPPORTED hatası döndürmelidir.

Bu dosya tanıtıcı seti, daha fazla fds (platforma özgü uzantıları uygulamak için) uygulamak için genişletilebilse de, dosya tanımlayıcılarını genişletmek için dikkatli olunmalıdır. Dosya tanımlayıcılarını genişletmek, çakışmalara neden olabilir ve genellikle önerilmez.

Dosya Tanımlayıcı API'sindeki Yöntemler

okuman()

Belirtilen bir dosya tanıtıcısından veri baytlarını count için okumaya çalışır.

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

[içinde] fd : İçinden okunacak dosya tanımlayıcısı

[out] buf : Verilerin depolanacağı bir arabelleğe işaretçi

[in] count : Okunacak maksimum bayt sayısı

[retval]: Döndürülen okunan bayt sayısı; aksi halde olumsuz bir hata

yazmak()

Belirtilen dosya tanıtıcısına veri baytlarını count kadar yazar.

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

[içinde] fd : Yazılacak dosya tanımlayıcısı

[out] buf : Yazılacak verilere işaretçi

[in] count : Yazılacak maksimum bayt sayısı

[retval]: Döndürülen bayt sayısı; aksi halde olumsuz bir hata

ioctl()

Belirli bir dosya tanıtıcısı için belirtilen bir ioctl komutunu çağırır.

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

[içinde] fd : ioctl() dosya tanımlayıcısı

[içinde] cmd : ioctl komutu

[giriş/çıkış] args : ioctl() argümanlarına işaretçi

Çeşitli API

Çeşitli API'deki Yöntemler

gettime()

Geçerli sistem saatini (nanosaniye cinsinden) döndürür.

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

[içinde] clock_id : Platforma bağlı; varsayılan için sıfırı geç

[içinde] flags : Ayrılmış, sıfır olmalıdır

[içinde] time : Geçerli saati depolamak için bir int64_t değerine işaretçi

[retval]: NO_ERROR ; aksi halde olumsuz bir hata

nano uyku()

Çağıran uygulamanın yürütülmesini belirli bir süre için askıya alır ve bu süreden sonra yeniden başlatır.

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

[içinde] clock_id : Ayrılmış, sıfır olmalıdır

[içinde] flags : Ayrılmış, sıfır olmalıdır

[içinde] sleep_time : Nanosaniye cinsinden uyku süresi

[retval]: NO_ERROR ; aksi halde olumsuz bir hata

Güvenilir bir uygulama sunucusu örneği

Aşağıdaki örnek uygulama, yukarıdaki API'lerin kullanımını göstermektedir. Örnek, birden çok gelen bağlantıyı işleyen ve güvenli veya güvenli olmayan taraftan gelen istemcilerden aldığı tüm mesajları arayan kişiye geri yansıtan bir "yankı" hizmeti oluşturur.

#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;
}

run_end_to_end_msg_test() yöntemi, "echo" hizmetine eşzamansız olarak 10.000 ileti gönderir ve yanıtları işler.

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;
}

Güvenli olmayan dünya API'leri ve uygulamaları

Güvenli taraftan yayınlanan ve IPC_PORT_ALLOW_NS_CONNECT özniteliği ile işaretlenen bir dizi Trusty hizmetine, güvenli olmayan tarafta çalışan çekirdek ve kullanıcı alanı programları tarafından erişilebilir.

Güvenli olmayan taraftaki yürütme ortamı (çekirdek ve kullanıcı alanı), güvenli taraftaki yürütme ortamından büyük ölçüde farklıdır. Bu nedenle, her iki ortam için tek bir kitaplık yerine, iki farklı API kümesi vardır. Çekirdekte, İstemci API'si güvenilir-ipc çekirdek sürücüsü tarafından sağlanır ve güvenli tarafta çalışan hizmetlerle iletişim kurmak için kullanıcı alanı süreçleri tarafından kullanılabilecek bir karakter aygıtı düğümünü kaydeder.

Kullanıcı alanı Güvenilir IPC İstemci API'si

Kullanıcı alanı Trusty IPC Client API kitaplığı, fd aygıt düğümünün üzerinde ince bir katmandır.

Bir kullanıcı alanı programı, tipc_connect() çağırarak, belirtilen bir Trusty hizmetine bir bağlantı başlatarak bir iletişim oturumu başlatır. Dahili olarak, tipc_connect() çağrısı, bir dosya tanıtıcısı elde etmek için belirtilen bir aygıt düğümünü açar ve bağlanılacak hizmet adını içeren bir dizeye işaret eden argp parametresiyle bir TIPC_IOC_CONNECT ioctl() çağrısını çağırır.

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

Ortaya çıkan dosya tanımlayıcı, yalnızca oluşturulduğu hizmetle iletişim kurmak için kullanılabilir. Dosya tanıtıcı, bağlantı artık gerekli olmadığında tipc_close() çağrılarak kapatılmalıdır.

tipc_connect() çağrısı tarafından elde edilen dosya tanıtıcı, tipik bir karakter aygıt düğümü gibi davranır; dosya tanıtıcısı:

  • Gerekirse engellenmeyen moda değiştirilebilir
  • Diğer tarafa mesaj göndermek için standart bir write() çağrısı kullanılarak yazılabilir
  • Gelen mesajların normal bir dosya tanımlayıcısı olarak kullanılabilirliği için yoklanabilir ( poll() çağrıları veya select() çağrıları kullanılarak)
  • Gelen mesajları almak için okunabilir

Arayan, belirtilen fd için bir yazma çağrısı yürüterek Trusty hizmetine bir mesaj gönderir. Yukarıdaki write() çağrısına iletilen tüm veriler, güvenilir-ipc sürücüsü tarafından bir mesaja dönüştürülür. İleti, verilerin Trusty çekirdeğindeki IPC alt sistemi tarafından işlendiği ve uygun hedefe yönlendirildiği ve belirli bir kanal tanıtıcısında bir IPC_HANDLE_POLL_MSG olayı olarak bir uygulama olay döngüsüne iletildiği güvenli tarafa iletilir. Belirli, hizmete özgü protokole bağlı olarak, Trusty hizmeti, güvenli olmayan tarafa geri gönderilen ve kullanıcı alanı uygulaması tarafından alınacak uygun kanal dosyası tanımlayıcı mesaj kuyruğuna yerleştirilen bir veya daha fazla yanıt mesajı gönderebilir read() arayın.

tipc_connect()

Belirtilen tipc aygıt düğümünü açar ve belirtilen bir Trusty hizmetine bağlantı başlatır.

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

[içinde] dev_name : Açılacak Güvenilir IPC cihaz düğümünün yolu

[içinde] srv_name : Bağlanılacak yayınlanmış bir Güvenilir hizmetin adı

[retval]: Başarı durumunda geçerli dosya tanımlayıcısı, aksi takdirde -1.

tipc_close()

Bir dosya tanıtıcısı tarafından belirtilen Trusty hizmetine olan bağlantıyı kapatır.

int tipc_close(int fd);

[içinde] fd : Daha önce bir tipc_connect() çağrısı ile açılmış dosya tanıtıcı

Çekirdek Güvenilir IPC İstemci API'si

Çekirdek Güvenilir IPC İstemci API'si, çekirdek sürücüleri için kullanılabilir. Kullanıcı alanı Trusty IPC API, bu API'nin üzerine uygulanır.

Genel olarak, bu API'nin tipik kullanımı, tipc_create_channel() işlevini kullanarak bir struct tipc_chan nesnesi oluşturan ve ardından güvenli tarafta çalışan Trusty IPC hizmetine bir bağlantı başlatmak için tipc_chan_connect() çağrısını kullanan bir arayandan oluşur. Uzak tarafla olan bağlantı, kaynakları temizlemek için tipc_chan_destroy() ve ardından tipc_chan_shutdown() çağrılarak sonlandırılabilir.

Bir bağlantının başarıyla kurulduğuna dair bir bildirim ( handle_event() geri çağrısı aracılığıyla) alındığında, arayan aşağıdakileri yapar:

  • tipc_chan_get_txbuf_timeout() çağrısını kullanarak bir mesaj arabelleği alır
  • Bir mesaj oluşturur ve
  • Kanalın bağlı olduğu bir Trusty hizmetine (güvenli tarafta) teslim için tipc_chan_queue_msg() yöntemini kullanarak mesajı kuyruğa alır

Kuyruğa alma başarılı olduktan sonra, mesaj arabelleği uzak taraf tarafından işlendikten sonra boş arabellek havuzuna geri döndüğünden (daha sonra yeniden kullanmak için, diğer mesajlar için) arayan kişinin mesaj arabelleğini unutması gerekir. Kullanıcının yalnızca tipc_chan_put_txbuf() bu tür bir arabelleği sıraya koyamaması veya artık gerekli olmaması durumunda çağırması gerekir.

Bir API kullanıcısı, işlenecek gelen bir iletiyi içeren bir rx arabelleğine bir işaretçi sağlayan bir handle_msg() bildirim geri çağrısı (güvenilir-ipc rx çalışma sırası bağlamında çağrılır) işleyerek uzak taraftan iletileri alır.

handle_msg() geri çağırma uygulamasının geçerli bir struct tipc_msg_buf için bir işaretçi döndürmesi beklenir. Yerel olarak işleniyorsa ve artık gerekli değilse, gelen ileti arabelleğiyle aynı olabilir. Alternatif olarak, gelen arabellek daha fazla işlem için kuyruğa alınmışsa, tipc_chan_get_rxbuf() çağrısıyla elde edilen yeni bir arabellek olabilir. Ayrılmış bir rx arabelleği izlenmeli ve sonunda, artık gerekmediğinde bir tipc_chan_put_rxbuf() çağrısı kullanılarak serbest bırakılmalıdır.

Kernel Trusty IPC Client API'deki Yöntemler

tipc_create_channel()

Belirli bir güvenilir-ipc aygıtı için bir Güvenilir IPC kanalı örneği oluşturur ve yapılandırır.

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

[içinde] dev : Cihaz kanalının oluşturulduğu güvenilir ipc'nin işaretçisi

[içinde] ops : struct tipc_chan_ops , arayana özel geri aramalar doldurulmuş

[içinde] cb_arg : tipc_chan_ops geri aramalarına iletilecek verilere işaretçi

[retval]: Başarı durumunda struct tipc_chan yeni oluşturulmuş örneğine işaretçi, aksi takdirde ERR_PTR(err)

Genel olarak, bir arayan, karşılık gelen etkinlik meydana geldiğinde eşzamansız olarak çağrılan iki geri arama sağlamalıdır.

void (*handle_event)(void *cb_arg, int event) olayı, arayanı kanal durumu değişikliği hakkında bilgilendirmek için çağrılır.

[içinde] cb_arg : tipc_create_channel() çağrısına iletilen verilere işaretçi

[in] event : Aşağıdaki değerlerden biri olabilen bir olay:

  • TIPC_CHANNEL_CONNECTED - uzak tarafa başarılı bir bağlantı olduğunu gösterir
  • TIPC_CHANNEL_DISCONNECTED - uzak tarafın yeni bağlantı talebini reddettiğini veya önceden bağlanan kanal için talep edilen bağlantının kesilmesini belirtir
  • TIPC_CHANNEL_SHUTDOWN - uzak tarafın kapatıldığını ve tüm bağlantıları kalıcı olarak sonlandırdığını gösterir

struct tipc_msg_buf *(*handle_msg)(void *cb_arg, struct tipc_msg_buf *mb) geri çağrısı yapısı, belirli bir kanal üzerinden yeni bir mesajın alındığına dair bildirim sağlamak için çağrılır:

  • [içinde] cb_arg : tipc_create_channel() çağrısına iletilen verilere işaretçi
  • [içinde] mb : Gelen bir mesajı açıklayan struct tipc_msg_buf işaretçisi
  • [retval]: Geri arama uygulamasının, mesaj yerel olarak işleniyorsa ve artık gerekli değilse (veya tarafından elde edilen yeni bir arabellek olabilir) bir mb parametresi olarak alınan aynı işaretçi olabilen bir struct tipc_msg_buf bir işaretçi döndürmesi beklenir. tipc_chan_get_rxbuf() çağrısı)

tipc_chan_connect()

Belirtilen Trusty IPC hizmetine bir bağlantı başlatır.

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

[içinde] chan : tipc_create_chan() çağrısı tarafından döndürülen bir kanalın işaretçisi

[içinde] port : Bağlanılacak hizmet adını içeren bir dizeye işaretçi

[retval]: Başarı durumunda 0, aksi takdirde olumsuz bir hata

Bir handle_event geri çağrısı alınarak bir bağlantı kurulduğunda arayan kişiye bildirilir.

tipc_chan_shutdown()

Daha önce bir tipc_chan_connect() çağrısı tarafından başlatılan Trusty IPC hizmetine olan bağlantıyı sonlandırır.

int tipc_chan_shutdown(struct tipc_chan *chan);

[içinde] chan : Bir tipc_create_chan() çağrısı tarafından döndürülen bir kanalın işaretçisi

tipc_chan_destroy()

Belirtilen bir Güvenilir IPC kanalını yok eder.

void tipc_chan_destroy(struct tipc_chan *chan);

[içinde] chan : tipc_create_chan() çağrısı tarafından döndürülen bir kanalın işaretçisi

tipc_chan_get_txbuf_timeout()

Belirtilen bir kanal üzerinden veri göndermek için kullanılabilecek bir mesaj arabelleği alır. Arabellek hemen mevcut değilse, arayan belirtilen zaman aşımı süresi boyunca (milisaniye olarak) engellenebilir.

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

[içinde] chan : Bir mesajın kuyruğa alınacağı kanalın işaretçisi

[içinde] chan : tx arabelleği kullanılabilir hale gelene kadar beklemek için maksimum zaman aşımı

[retval]: Başarı durumunda geçerli bir mesaj arabelleği, ERR_PTR(err)

tipc_chan_queue_msg()

Belirtilen Trusty IPC kanalları üzerinden gönderilmek üzere bir mesajı kuyruğa alır.

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

[içinde] chan : Mesajın kuyruğa alınacağı kanalın işaretçisi

[içinde] mb: Kuyruğa alınacak mesajın tipc_chan_get_txbuf_timeout() çağrısı ile elde edilir)

[retval]: Başarı durumunda 0, aksi takdirde olumsuz bir hata

tipc_chan_put_txbuf()

Bir tipc_chan_get_txbuf_timeout() çağrısı ile önceden elde edilen belirtilen Tx mesaj arabelleğini serbest bırakır.

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

[içinde] chan : Bu mesaj arabelleğinin ait olduğu kanalı gösteren işaretçi

[içinde] mb : Serbest bırakılacak mesaj arabelleğine işaretçi

[geri dönüş]: Yok

tipc_chan_get_rxbuf()

Belirtilen kanal üzerinden mesajları almak için kullanılabilecek yeni bir mesaj arabelleği alır.

struct tipc_msg_buf *tipc_chan_get_rxbuf(struct tipc_chan *chan);

[içinde] chan : Bu mesaj arabelleğinin ait olduğu bir kanalın işaretçisi

[retval]: Başarı durumunda geçerli bir mesaj arabelleği, ERR_PTR(err)

tipc_chan_put_rxbuf()

Daha önce bir tipc_chan_get_rxbuf() çağrısı ile elde edilen belirli bir mesaj arabelleğini serbest bırakır.

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

[içinde] chan : Bu mesaj arabelleğinin ait olduğu bir kanalın işaretçisi

[içinde] mb : Serbest bırakılacak bir mesaj arabelleğine işaretçi

[geri dönüş]: Yok