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ın sağladığı hizmetleri kullanan normal/güvenilmeyen uygulamalar

Güvenilir API genel olarak, güvenli olmayan dünyayla iletişimler de dahil olmak üzere Güvenilir süreçler arası iletişim (IPC) sistemini tanımlar. Ana işlemcide ç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'leri kullanabilir. Bu mesajların veri formatını ve anlamını uygulama düzeyinde bir protokol kullanarak belirlemek uygulamaya kalmıştır. Mesajların güvenilir şekilde iletilmesi, 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ı, Trusty uygulamaları tarafından hizmet uç noktalarını istemcilerin bağlandığı adlandırılmış bir yol biçiminde ortaya çıkarmak için kullanılır. Bu, istemcilerin kullanması için basit, dize tabanlı bir hizmet kimliği sağlar. 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. Hizmetin gelen bir bağlantıyı kabul etmesi gerekir ve bunu yaptığında kendisi de bir kanal alır. Temelde, bağlantı noktaları hizmetleri aramak için kullanılır ve ardından iletişim, bir çift bağlı kanal (yani, bir bağlantı noktası üzerindeki bağlantı örnekleri) üzerinden gerçekleşir. Bir istemci 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 isteğe bağlı mesaj alışverişinde bulunabilir.

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ın yayınladığı hizmetlere 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

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

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 belirtilmediği sürece bir tanıtıcının değerini diğer uygulamalara aktarmamalıdır. 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 tanıtıcıyla ilişkilendirir.

long set_cookie(uint32_t handle, void *cookie)

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

[içinde] cookie : Trusty uygulamasındaki isteğe bağlı kullanıcı alanı verilerinin işaretçisi

[retval]: Başarı durumunda NO_ERROR , aksi halde < 0 hata kodu

Bu çağrı, tanıtıcı oluşturulduktan sonra daha sonra meydana gelen olayların işlenmesi için kullanışlıdır. Olay işleme mekanizması, tanıtıcıyı ve onun çerezini olay işleyiciye geri sağlar.

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

Beklemek()

Belirtilen süre boyunca belirli bir tanıtıcıda bir olayın oluşmasını bekler.

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

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

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

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

[retval]: Belirtilen zaman aşımı aralığı içinde geçerli bir olay meydana gelirse NO_ERROR ; ERR_TIMED_OUT , belirli bir zaman aşımı süresi geçmişse ancak hiçbir olay meydana gelmemişse; diğer hatalar için < 0

Başarı durumunda ( retval == NO_ERROR ), wait() çağrısı belirli 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 - aslında beklemede olan hiçbir olay yok, arayan kişinin beklemeyi yeniden başlatması gerekir

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ının olduğunu gösterir
  • Kanallar için bu değer, eşzamansız bir bağlantının (bkz. connect() ) kurulduğunu gösterir.

Aşağıdaki etkinlikler yalnızca kanallarla ilgilidir:

  • IPC_HANDLE_POLL_HUP - 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 - daha önce gönderimi engellenen arayanın tekrar mesaj göndermeyi deneyebileceğini belirtir (ayrıntılar için send_msg() açıklamasına bakın)

Aynı anda birden fazla bit ayarlanabileceğinden, belirtilen olayların bir kombinasyonunu işlemek için bir olay işleyicisi hazırlanmalıdır. Örneğin, bir kanal için aynı anda bekleyen mesajların olması ve bir eşin bağlantının kapatılması mümkündür.

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

Tutamaçlar close() yöntemi çağrılarak yok edilebilir.

kapalı()

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

long close(uint32_t handle_id);

[içinde] handle_id : Yok edilecek tutamaç

[retval]: Başarılıysa 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ıyla 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 tahsis edebileceği maksimum arabellek sayısı. Her iki yönde giden veriler için arabellekler ayrı ayrı sayılır; dolayısıyla 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 ile sunucu arasındaki üst düzey protokol anlaşmasına bağlıdır. Çok senkronize bir protokol (mesaj gönder, cevap al, sonra başka bir mesaj gönder) durumunda bu sayı 1 kadar küçük olabilir. Ancak müşteri bir yanıtın ortaya çıkmasından önce birden fazla mesaj göndermeyi bekliyorsa bu sayı daha fazla olabilir (örneğin, bir mesaj giriş olarak ve diğeri gerçek komut olarak). Tahsis edilen arabellek kümeleri kanal başınadır, dolayısıyla iki ayrı bağlantının (kanalların) ayrı arabellek kümeleri olacaktır.

[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şlerle paylaşabileceğiniz maksimum mesaj boyutunu etkili bir şekilde sınırlar

[içinde] flags : Ek bağlantı noktası davranışını belirten bayrakların birleşimi

Bu değer aşağıdaki değerlerin birleşimi 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 veya negatifse belirli bir hata varsa oluşturulan bağlantı noktasını yönetin

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 IPC_HANDLE_POLL_READY bit seti tarafından belirtilen bir bağlantı isteği alındığında, sunucu, bağlantı kurmayı bitirmek ve daha sonra gelen mesajlar için yoklanabilecek bir kanal (başka bir tanıtıcı tarafından temsil edilen) oluşturmak için accept() öğesini çağırmalı .

kabul etmek()

Gelen bir bağlantıyı kabul eder ve bir kanalın tanıtıcısını 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 uuid_t yapısının işaretçisi. Bağlantının güvenli olmayan bir dünyadan gelmesi durumunda tamamı sıfıra ayarlanacaktır.

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

İstemci API'si

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 bağlantı noktasının adı

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

[retval]: Sunucuyla mesajların değiş tokuş edilebileceği bir kanalı yönetir; negatif ise hata

Herhangi bir flags belirtilmezse ( flags parametresi 0'a ayarlanır), connect() işlevinin çağrılması, belirtilen bir bağlantı noktasına senkronize bir bağlantı başlatır; bu bağlantı noktası mevcut değilse hemen bir hata döndürür 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 birleşimi belirtilerek 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 mevcut değilse, hemen başarısız olmak yerine connect() çağrısını beklemeye zorlar.

IPC_CONNECT_ASYNC - ayarlandıysa 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 ayarlanan IPC_HANDLE_POLL_READY biti tarafından belirtilen bir bağlantı tamamlama olayı için wait() öğesini çağırarak döndürülen tanıtıcıyı yoklaması gerekir.

Mesajlaşma API'si

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

Bir istemci, connect() çağrısı yaparak bir kanalın tanıtıcısını alır ve yukarıda açıklandığı gibi, sunucu da 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 minimal bir yapıya sahiptir ve asıl içeriğin anlambilimi üzerinde anlaşmaya varma yetkisini sunucuya 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ıyla temsil edilen bir veya daha fazla bitişik olmayan ara bellekten oluşabilir. Trusty, iov dizisini kullanarak bu bloklara dağılım-toplama okuma ve yazma işlemleri gerçekleştirir. iov dizisi tarafından tanımlanabilen arabelleklerin içeriği tamamen isteğe bağlıdır.

Messaging API'deki yöntemler

send_msg()

Belirtilen kanal üzerinden mesaj gönderir.

long send_msg(uint32_t handle, ipc_msg_t *msg);

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

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

[retval]: Başarı durumunda 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önderim engellenmiş duruma girebilir (basit bir senkronize istek/yanıt protokolünde bu asla gerçekleşmemelidir) ancak daha karmaşık durumlarda meydana gelebilir) bu, bir ERR_NOT_ENOUGH_BUFFER hata kodunun döndürülmesiyle gösterilir. Böyle bir durumda arayanın wait() çağrısı tarafından döndürülen uevent_t yapısının event alanında ayarlanan IPC_HANDLE_POLL_SEND_UNBLOCKED biti tarafından belirtilen işleme ve kullanımdan kaldırma mesajlarını alarak eşin alma kuyruğunda bir miktar yer açmasını beklemesi gerekir.

get_msg()

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

Belirtilen 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 : Mesaj bilgi yapısı aşağıda açıklanmıştır:

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

Bekleyen mesajlar kümesi boyunca her mesaja 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çık) mesaj olabilir.

[retval]: Başarı durumunda NO_ERROR ; aksi halde olumsuz bir hata

read_msg()

Belirtilen ofsetten 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 göre ofset

[out] msg : Gelen mesaj verilerinin depolanacağı bir dizi arabelleği açıklayan ipc_msg_t yapısına işaretçi

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

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

put_msg()

Belirtilen kimliğe sahip bir iletiyi kullanımdan kaldırır.

long put_msg(uint32_t handle, uint32_t msg_id);

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

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

[retval]: Başarı durumunda NO_ERROR ; aksi halde olumsuz bir hata

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

Dosya Tanımlayıcı API'si

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 dizi dosya tanımlayıcı üzerinde çalışabilir. Mevcut uygulamada dosya tanımlayıcı alanı IPC tanıtıcı alanından ayrıdır. Trusty'deki Dosya Tanımlayıcı API'si, geleneksel 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 giriş fd varsayılan uygulaması işlemsizdir (güvenilir uygulamaların etkileşimli bir konsola sahip olması beklenmediğinden), bu nedenle fd 0'da ioctl() okumak, yazmak veya çağırmak bir ERR_NOT_SUPPORTED hatası döndürmelidir.
  • 1 - standart çıktı. Standart çıkışa yazılan veriler, platform ve konfigürasyona bağlı olarak (LK hata ayıklama düzeyine bağlı olarak) UART'a ve/veya 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 ERR_NOT_SUPPORTED hatası döndürmelidir.
  • 2 - standart hata. Standart hataya yazılan veriler, platform ve yapılandırmaya bağlı olarak güvenli olmayan tarafta bulunan UART'a veya bellek günlüğüne yönlendirilmelidir. Bu akışın kısıtlanmama ihtimali çok yüksek olduğundan standart hataya yalnızca kritik mesajların yazılması önerilir. read() ve ioctl() yöntemleri işlem gerektirmez ve ERR_NOT_SUPPORTED hatası döndürmelidir.

Bu dosya tanımlayıcı kümesi daha fazla fds uygulamak için (platforma özgü uzantıları uygulamak için) genişletilebilse de, dosya tanımlayıcılarının genişletilmesinin dikkatli bir şekilde uygulanması gerekir. Dosya tanımlayıcılarının genişletilmesi çakışmalara neden olabilir ve genellikle önerilmez.

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

Okumak()

Belirtilen dosya tanımlayıcıdan count veriyi okumaya çalışır.

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

[içinde] fd : Okunacak dosya tanımlayıcı

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

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

[retval]: Okunan bayt sayısı döndürülür; aksi halde olumsuz bir hata

yazmak()

Belirtilen dosya tanımlayıcıya baytlık veriyi count .

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

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

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

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

[retval]: Yazılan bayt sayısı döndürülür; aksi halde olumsuz bir hata

ioctl()

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

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

[içinde] fd : ioctl() işlevinin çağrılacağı dosya tanımlayıcı

[içinde] cmd : ioctl komutu

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

Ç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, int64_t *time);

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

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

[out] time : Geçerli saatin saklanacağı int64_t değerinin işaretçisi

[retval]: Başarı durumunda NO_ERROR ; aksi halde olumsuz bir hata

nano uyku()

Çağıran uygulamanın yürütülmesini belirli bir süreliğine askıya alır ve bu sürenin sonunda devam ettirir.

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

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

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

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

[retval]: Başarı durumunda 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ı yöneten ve güvenli veya güvenli olmayan taraftan gelen istemcilerden aldığı tüm iletileri 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 mesaj 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ğiyle işaretlenen bir dizi Güvenilir hizmete, 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ı işlemleri tarafından kullanılabilen 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 üstünde ince bir katmandır.

Bir kullanıcı alanı programı, tipc_connect() öğesini çağırarak ve belirli bir Güvenilir hizmete bağlantı başlatarak bir iletişim oturumu başlatır. Dahili olarak tipc_connect() çağrısı, bir dosya tanımlayıcı 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 parametresi ile 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. Bağlantı artık gerekli olmadığında dosya tanımlayıcı tipc_close() çağrılarak kapatılmalıdır.

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

  • Gerektiğinde engellemesiz moda geçilebilir
  • Diğer tarafa mesaj göndermek için standart write() çağrısı kullanılarak yazılabilir
  • Gelen mesajların normal bir dosya tanımlayıcı olarak kullanılabilirliği için yoklama yapılabilir ( 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ı gerçekleştirerek Trusty hizmetine bir mesaj gönderir. Yukarıdaki write() çağrısına aktarılan tüm veriler güvenilir ipc sürücüsü tarafından bir mesaja dönüştürülür. Mesaj, 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ındaki 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, Güvenilir hizmet, güvenli olmayan tarafa geri gönderilen ve kullanıcı alanı uygulamasının read() Arama.

tipc_connect()

Belirtilen tipc cihaz düğümünü açar ve belirtilen Güvenilir hizmete 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 aygıt düğümünün yolu

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

[retval]: Başarılı olduğunda geçerli dosya tanımlayıcı, aksi durumda -1.

tipc_close()

Bir dosya tanımlayıcı tarafından belirtilen Güvenilir hizmetine olan bağlantıyı kapatır.

int tipc_close(int fd);

[içinde] fd : Daha önce tipc_connect() çağrısıyla açılan dosya tanımlayıcı

Çekirdek Güvenilir IPC İstemci API'si

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

Genel olarak, bu API'nin tipik kullanımı, arayanın tipc_create_channel() işlevini kullanarak bir struct tipc_chan nesnesi oluşturmasından ve ardından güvenli tarafta çalışan Trusty IPC hizmetiyle bir bağlantı başlatmak için tipc_chan_connect() çağrısını kullanmasından oluşur. Uzak taraftaki bağlantı, kaynakları temizlemek için tipc_chan_shutdown() çağrıldıktan sonra tipc_chan_destroy() çağrılarak sonlandırılabilir.

Bağlantının başarılı bir şekilde kurulduğuna dair bir bildirim ( handle_event() geri çağrısı aracılığıyla) aldıktan sonra arayan kişi aşağıdakileri yapar:

  • tipc_chan_get_txbuf_timeout() çağrısını kullanarak bir mesaj arabelleği elde eder
  • Bir mesaj oluşturur ve
  • Kanalın bağlı olduğu Güvenilir hizmete (güvenli tarafta) teslim etmek için tipc_chan_queue_msg() yöntemini kullanarak mesajı sıraya koyar

Kuyruğa alma başarılı olduktan sonra, arayan kişinin mesaj arabelleğini unutması gerekir çünkü mesaj arabelleği, uzak taraf tarafından işlendikten sonra (daha sonra diğer mesajlar için yeniden kullanılmak üzere) sonunda boş arabellek havuzuna geri döner. Kullanıcının yalnızca böyle bir arabelleği sıraya koyamaması veya artık gerekli olmaması durumunda tipc_chan_put_txbuf() u çağırması gerekir.

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

handle_msg() geri çağırma uygulamasının geçerli struct tipc_msg_buf bir işaretçi döndürmesi beklenir. Yerel olarak işleniyorsa ve artık gerekli değilse, gelen mesaj arabelleğiyle aynı olabilir. Alternatif olarak, eğer gelen arabellek daha ileri işlemler 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 artık gerekmediğinde tipc_chan_put_rxbuf() çağrısı kullanılarak sonunda serbest bırakılmalıdır.

Çekirdek Güvenilir IPC İstemci API'sindeki yöntemler

tipc_create_channel()

Belirli bir güvenilir ipc cihazı için Güvenilir IPC kanalının bir örneğini 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 : Arayana özel geri aramaların doldurulmuş olduğu bir struct tipc_chan_ops işaretçisi

[içinde] cb_arg : tipc_chan_ops geri çağrılarına aktarılacak veri işaretçisi

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

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

void (*handle_event)(void *cb_arg, int event) olayı, arayan kişiyi 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 eden işaretçi

[içinde] event : Aşağıdaki değerlerden biri olabilecek bir olay:

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

struct tipc_msg_buf *(*handle_msg)(void *cb_arg, struct tipc_msg_buf *mb) geri çağrı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 eden işaretçi
  • [içinde] mb : Gelen bir mesajı açıklayan struct tipc_msg_buf işaretçisi
  • [retval]: Geri çağırma uygulamasının, eğer mesaj yerel olarak işleniyorsa ve artık gerekli değilse (veya bu, mesaj yerel olarak elde edilen yeni bir arabellek olabilir) bir mb parametresi olarak alınan işaretçiyle aynı olabilecek bir struct tipc_msg_buf yapısına bir işaretçi döndürmesi beklenir. tipc_chan_get_rxbuf() çağrısı)

tipc_chan_connect()

Belirtilen Güvenilir IPC hizmetine 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 dizenin işaretçisi

[retval]: Başarı durumunda 0, aksi halde negatif hata

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

tipc_chan_shutdown()

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

int tipc_chan_shutdown(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_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()

Belirli bir kanal üzerinden veri göndermek için kullanılabilecek bir mesaj arabelleği elde eder. Arabellek hemen kullanılamıyorsa, arayan kişi belirtilen zaman aşımı süresi boyunca (milisaniye cinsinden) engellenebilir.

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

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

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

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

tipc_chan_queue_msg()

Belirtilen Güvenilir IPC kanalları üzerinden gönderilecek bir mesajı sıraya koyar.

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 işaretçisi ( tipc_chan_get_txbuf_timeout() çağrısıyla elde edilir)

[retval]: Başarı durumunda 0, aksi halde negatif hata

tipc_chan_put_txbuf()

Daha önce tipc_chan_get_txbuf_timeout() çağrısıyla 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ğinin işaretçisi

[geri alma]: Yok

tipc_chan_get_rxbuf()

Belirtilen kanal üzerinden mesaj 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 kanalın işaretçisi

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

tipc_chan_put_rxbuf()

Daha önce tipc_chan_get_rxbuf() çağrısıyla 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 kanalın işaretçisi

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

[geri alma]: Yok