Trusty API referansı

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

  • TEE işlemcisinde çalışan güvenilir uygulamalar ve hizmetler
  • Ana işlemcide çalışan ve güvenilir uygulamaların sağladığı hizmetleri kullanan normal ve güvenilmeyen uygulamalar

Trusty API'si, genellikle güvenli olmayan dünyayla yapılan iletişimler dahil olmak üzere Trusty işlemler arası iletişim (IPC) sistemini tanımlar. Ana işlemcide çalışan yazılımlar, güvenilir uygulamalara ve hizmetlere bağlanmak ve IP üzerinden bir ağ hizmeti gibi onlarla rastgele mesaj alışverişinde bulunmak için Güvenilir API'leri 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 teslim edilmesi, temel Trusty altyapısı (ana işlemcide ç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ı, hizmet uç noktalarını müşterilerin bağlanacağı adlandırılmış bir yol biçiminde göstermek için Güvenilir uygulamalar tarafından kullanılır. Bu işlem, istemcilerin kullanabileceği basit, dize tabanlı bir hizmet kimliği sağlar. Adlandırma kuralı, ters DNS stilinde adlandırmadır (ör. com.google.servicename).

Bir istemci bir bağlantı noktasına bağlandığında, hizmetle etkileşime geçmek için bir kanal alır. Hizmet, gelen bir bağlantıyı kabul etmelidir. Bu durumda, hizmet de bir kanal alır. Bağlantı noktaları temel olarak hizmetleri aramak için kullanılır ve ardından iletişim, bağlı bir kanal çifti (ör. bağlantı örneği) olabilir. Bir istemci bir bağlantı noktasına bağlandığında simetrik, iki yönlü bir bağlantı kurulur. Bu tam çift yönlü yolu kullanarak istemciler ve sunucular, herhangi bir taraf bağlantıyı kaldırmaya karar verene kadar istedikleri iletileri paylaşabilir.

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

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

Handle API

Herkese açık anahtarlar, UNIX'teki dosya tanımlayıcılarına benzer şekilde bağlantı noktası ve kanal gibi kaynakları temsil eden işaretsiz tam sayılardır. Herkese açık kullanıcı adları oluşturulduktan sonra uygulamaya özel bir herkese açık kullanıcı adı tablosuna yerleştirilir ve daha sonra referans olarak kullanılabilir.

Bir araya getiren, set_cookie() yöntemini kullanarak özel verileri herkese açık kullanıcı adıyla ilişkilendirebilir.

Handle API'deki yöntemler

Herkese açık kullanıcı adları yalnızca bir uygulama bağlamında geçerlidir. Bir uygulama, açıkça belirtilmediği sürece herkese açık kullanıcı adının değerini diğer uygulamalara iletmemelidir. Herkese açık kullanıcı adı değeri yalnızca, bir uygulamanın herkese açık kullanıcı adının geçersiz veya ayarlanmamış olduğunu belirtmek için kullanabileceği INVALID_IPC_HANDLE #define, ile karşılaştırılarak yorumlanmalıdır.

Arayanın sağladığı özel verileri belirtilen bir herkese açık kullanıcı adıyla ilişkilendirir.

long set_cookie(uint32_t handle, void *cookie)

[in] handle: API çağrılarından biri tarafından döndürülen herhangi bir herkese açık kullanıcı adı

[in] cookie: Trusty uygulamasındaki kullanıcı alanındaki rastgele verilere işaretçi

[retval]: Başarılı olursa NO_ERROR, aksi takdirde < 0 hata kodu

Bu çağrı, herkese açık kullanıcı adı oluşturulduktan sonra daha sonra gerçekleşen etkinlikleri işlemek için yararlıdır. Etkinlik işleyici mekanizması, imleci ve çerezini etkinlik işleyiciye geri gönderir.

wait() çağrısı kullanılarak etkinlikler için tutamak beklenebilir.

wait()

Belirli bir tutamakta belirtilen süre boyunca bir etkinliğin gerçekleşmesini bekler.

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

[in] handle_id: API çağrılarından biri tarafından döndürülen herhangi bir herkese açık kullanıcı adı

[out] event: Bu herkese açık kullanıcı adında gerçekleşen bir etkinliği temsil eden yapının işaretçisi

[in] timeout_msecs: Milisaniye cinsinden zaman aşımı değeri; -1 değeri sonsuz zaman aşımı anlamına gelir

[retval]: Belirtilen zaman aşımı aralığında geçerli bir etkinlik gerçekleştiyse NO_ERROR; belirtilen zaman aşımı dolduysa ancak etkinlik gerçekleşmediyse ERR_TIMED_OUT; diğer hatalar için < 0

Başarılı olduğunda (retval == NO_ERROR), wait() çağrısı, belirtilen bir uevent_t yapısını gerçekleşen etkinlikle 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: Beklemedeki etkinlik yok. Arayıcı beklemeyi yeniden başlatmalıdır.

IPC_HANDLE_POLL_ERROR: Belirtilmemiş bir dahili hata oluştu

IPC_HANDLE_POLL_READY: Herkese açık kullanıcı adı türüne bağlı olarak aşağıdaki gibidir:

  • 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 (connect() bölümüne bakın) kurulduğunu gösterir.

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

  • IPC_HANDLE_POLL_HUP: Bir kanalın 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 mesaj göndermesi engellenen bir arayanın tekrar mesaj göndermeye çalışabileceğini gösterir (Ayrıntılar için send_msg() açıklamasına bakın)

Aynı anda birden fazla bit ayarlanabileceğinden, etkinlik işleyici belirli etkinliklerin bir kombinasyonunu işlemeye hazır olmalıdır. Örneğin, bir kanalda bekleyen iletiler olabilir ve aynı anda bir eş tarafından bağlantı kapatılabilir.

Çoğu etkinlik yapışkandır. Bu tür mesajlar, temel koşul devam ettiği sürece (ör. bekleyen tüm mesajlar alındıktan ve bekleyen bağlantı istekleri işlendikten sonra) devam eder. Bunun istisnası, IPC_HANDLE_POLL_SEND_UNBLOCKED etkinliğidir. Bu etkinlik, okunur okunmaz temizlenir ve uygulamanın bu etkinliği işlemesi için yalnızca bir şansı vardır.

Herkese açık kullanıcı adları, close() yöntemi çağrılarak kaldırılabilir.

close()

Belirtilen herkese açık kullanıcı adıyla ilişkili kaynağı siler ve herkese açık kullanıcı adı tablosundan kaldırır.

long close(uint32_t handle_id);

[in] handle_id: Kaldırılacağı herkese açık kullanıcı adı

[retval]: Başarılıysa 0, aksi takdirde negatif bir hata

Sunucu API'si

Sunucular, 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 tutamaçla temsil edilir.

Server API'deki 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)

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

[in] 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ı. Tamponlar, her iki yönde de giden veriler için ayrı ayrı sayılır. Bu nedenle, burada 1 belirtilmesi, 1 gönderme ve 1 alma tamponunun önceden ayrıldığı anlamına gelir. Genel olarak, gerekli arabellek sayısı istemci ile sunucu arasındaki üst düzey protokol sözleşmesine bağlıdır. Çok senkronize bir protokol söz konusu olduğunda bu sayı 1'e kadar düşebilir (mesaj gönder, başka bir mesaj göndermeden önce yanıt al). Ancak istemci, yanıt gösterilmeden önce birden fazla mesaj göndermeyi bekliyorsa (ör.giriş olarak bir mesaj, gerçek komut olarak başka bir mesaj) bu sayı daha fazla olabilir. Ayrılan arabellek kümeleri kanal başınadır. Bu nedenle, iki ayrı bağlantının (kanal) ayrı arabellek kümeleri olur.

[in] recv_buf_size: Yukarıdaki arabellek grubundaki her bir arabelleğin maksimum boyutu. Bu değer protokole bağlıdır ve eşle alışveriş yapabileceğiniz maksimum ileti boyutunu etkili bir şekilde sınırlar

[in] flags: Ek bağlantı noktası davranışını belirten işaretlerin bir 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ı kurulmasına izin verir.

IPC_PORT_ALLOW_NS_CONNECT: Güvenli olmayan dünyadan bağlantıya izin verir.

[retval]: Negatif olmayan değerlerde oluşturulan bağlantı noktasının, negatif değerlerde ise belirli bir hatanın mülk adı

Ardından sunucu, wait() çağrısını kullanarak gelen bağlantılar için bağlantı noktası tutamaçlarının listesini sorar. uevent_t yapısının event alanında ayarlanan IPC_HANDLE_POLL_READY bitiyle belirtilen bir bağlantı isteği aldıktan sonra sunucu, bağlantı oluşturmayı tamamlamak ve gelen mesajlar için sorgulanabilir bir kanal (başka bir tutamaçla temsil edilir) oluşturmak üzere accept() işlevini çağırmalıdır.

accept()

Gelen bağlantıyı kabul eder ve kanalın herkese açık kullanıcı adını alır.

long accept(uint32_t handle_id, uuid_t *peer_uuid);

[in] handle_id: İstemcinin bağlandığı bağlantı noktasını temsil eden işleyici

[out] peer_uuid: Bağlanan istemci uygulamasının UUID'siyle doldurulacak bir uuid_t yapısının işaretçisi. Bağlantı güvenli olmayan dünyadan kaynaklanıyorsa tümüyle sıfır olarak ayarlanır.

[retval]: Sunucunun istemciyle mesaj alışverişi yapabileceği bir kanalın (negatif olmayansa) adı (aksi takdirde bir hata kodu)

Client API

Bu bölümde, İstemci API'sindeki yöntemler yer alır.

İstemci API'sindeki yöntemler

connect()

Adıyla belirtilen bir bağlantı noktasına bağlantı başlatır.

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

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

[in] flags: Ek, isteğe bağlı davranışı belirtir

[retval]: Sunucuyla mesaj alışverişi yapılabilecek bir kanalın herkese açık kullanıcı adı; negatif ise hata

Hiçbir flags belirtilmezse (flags parametresi 0 olarak ayarlanırsa) connect() çağrısı, belirtilen bir bağlantı noktasına senkronize bir bağlantı başlatır. Bağlantı noktası mevcut değilse hemen hata döndürülür ve sunucu başka bir şekilde bağlantı kabul edene kadar bir engelleme oluşturulur.

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 mevcut değilse connect() çağrısının hemen başarısız olması yerine beklemesini zorlar.

IPC_CONNECT_ASYNC: Ayarlanırsa ayarsız bağlantı başlatır. Uygulamanın, normal çalışmaya başlamadan önce uevent_t yapısının etkinlik alanında ayarlanan IPC_HANDLE_POLL_READY biti tarafından belirtilen bir bağlantı tamamlama etkinliği için wait()'ü çağırarak döndürülen imleci kontrol etmesi gerekir.

Messaging API

Messaging API çağrıları, daha önce kurulmuş bir bağlantı (kanal) üzerinden mesaj göndermeyi ve okumayı sağlar. Messaging API çağrıları, sunucular ve istemciler için aynıdır.

İstemci, connect() çağrısı göndererek bir kanalın herkese açık kullanıcı adını alır. Sunucu ise yukarıda açıklanan accept() çağrısından kanal herkese açık kullanıcı adını alır.

Güvenilir mesajın yapısı

Aşağıda gösterildiği gibi, Trusty API tarafından paylaşılan mesajlar minimum bir yapıya sahiptir. Gerçek içeriğin anlamsal özelliklerini belirleme görevi ise sunucuya ve istemciye bırakılı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 ileti, iovec_t yapıları dizisiyle temsil edilen bir veya daha fazla bitişik olmayan arabellekten oluşabilir. Trusty, iov dizisini kullanarak bu bloklarda dağıtılmış toplama okuma ve yazma işlemleri gerçekleştirir. iov dizisi tarafından açıklanabilen arabelleğe alınan verilerin içeriği tamamen keyfidir.

Mesajlaşma API'sindeki yöntemler

send_msg()

Belirtilen bir kanal üzerinden mesaj gönderir.

long send_msg(uint32_t handle, ipc_msg_t *msg);

[in] handle: İletinin gönderileceği kanalın herkese açık kullanıcı adı

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

[retval]: Başarılı olduğunda gönderilen toplam bayt sayısı; aksi takdirde negatif bir hata

Müşteri (veya sunucu) kanal üzerinden mesaj göndermeye çalışıyorsa ve hedef eş mesaj kuyruğunda yer yoksa kanal, gönderme engellendi durumuna girebilir (bu durum basit bir senkronize istek/yanıt protokolünde hiçbir zaman yaşanmaz ancak daha karmaşık durumlarda yaşanabilir). Bu durum, ERR_NOT_ENOUGH_BUFFER hata kodu döndürülerek gösterilir. Böyle bir durumda arayan, 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 bitiyle gösterilen işleme ve emekliye alma mesajlarını alarak eşlemenin alma kuyruğunda yer açmasını beklemelidir.

get_msg()

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

belirli bir kanalın

long get_msg(uint32_t handle, ipc_msg_info_t *msg_info);

[in] handle: Yeni mesajın alınması gereken kanalın herkese açık kullanıcı adı

[out] msg_info: Mesaj bilgileri yapısı aşağıdaki gibi açıklanır:

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

Her iletiye, bekleyen ileti grubu içinde benzersiz bir kimlik atanır ve her ileti için toplam uzunluk doldurulur. Protokol tarafından yapılandırılmışsa ve izin verilmişse belirli bir kanal için aynı anda birden fazla bekleyen (açık) mesaj olabilir.

[retval]: Başarılı olduğunda NO_ERROR, aksi takdirde negatif bir hata

read_msg()

Belirtilen kimliğe sahip ileti içeriğini, belirtilen ofsetten itibaren okur.

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

[in] handle: İletinin okunacağı kanalın herkese açık kullanıcı adı

[in] msg_id: Okunacak ileti kimliği

[in] offset: Okumaya başlanacak mesajdaki ofset

[out] msg: Gelen mesaj verilerinin depolanacağı bir dizi arabelleği tanımlayan ipc_msg_t yapısının işaretçisi

[retval]: Başarılı olduğunda msg arabelleklerinde depolanan toplam bayt sayısı; aksi takdirde negatif bir hata

read_msg yöntemi, gerektiğinde farklı bir (sıralı olmayabilir) 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);

[in] handle: İletinin gönderildiği kanalın herkese açık kullanıcı adı

[in] msg_id: Kullanımdan kaldırılan ileti kimliği

[retval]: Başarılı olduğunda NO_ERROR, aksi takdirde negatif bir hata

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

File Descriptor API

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ıları kümesinde çalışabilir. Mevcut uygulamada, dosya tanımlayıcı alanı IPC tutamaç alanından ayrıdır. Trusty'deki File Descriptor API, geleneksel dosya tanımlayıcı tabanlı API'ye benzer.

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

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

Bu dosya tanımlayıcı grubu daha fazla fds (platforma özgü uzantıları uygulamak için) uygulamak üzere genişletilebilir olsa da dosya tanımlayıcıları genişletirken dikkatli olunmalıdır. Dosya tanımlayıcılarının uzatılması çakışmalara yol açabileceğinden genellikle önerilmez.

File Descriptor API'deki yöntemler

read()

Belirli bir dosya tanımlayıcısından count bayta kadar veri okumayı dener.

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

[in] fd: Okunacak dosya tanımlayıcı

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

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

[retval]: Okunan bayt sayısı döndürülür; aksi takdirde negatif bir hata döndürülür.

write()

Belirtilen dosya tanımlayıcısı için en fazla count bayt veri yazar.

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

[in] fd: Yazılacak dosya tanımlayıcı

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

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

[retval]: Yazılan bayt sayısı döndürülür. Aksi takdirde negatif bir hata döndürülür.

ioctl()

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

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

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

[in] cmd: ioctl komutu

[in/out] args: ioctl() bağımsız değişkenlerinin 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);

[in] clock_id: Platforma bağlıdır; varsayılan olarak sıfır değerini iletin

[in] flags: Ayrılmış, sıfır olmalıdır

[out] time: Mevcut zamanın depolanacağı bir int64_t değerinin işaretçisi

[retval]: Başarılı olduğunda NO_ERROR, aksi takdirde negatif bir hata

nanosleep()

Arayan uygulamanın yürütülmesini belirli bir süre için askıya alır ve bu sürenin ardından devam ettirir.

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

[in] clock_id: Ayrılmış, sıfır olmalıdır

[in] flags: Ayrılmış, sıfır olmalıdır

[in] sleep_time: Nanosaniye cinsinden uyku süresi

[retval]: Başarılı olduğunda NO_ERROR, aksi takdirde negatif bir hata

Güvenilir uygulama sunucusu örneği

Aşağıdaki örnek uygulamada, yukarıdaki API'lerin kullanımı gösterilmektedir. Sana Özel, birden fazla gelen bağlantıyı işleyen ve güvenli veya güvenli olmayan taraftan gelen istemcilerden aldığı tüm iletileri arayana geri yansıtan bir "yansıma" 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 app 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 10.000 mesajı eşzamansız olarak 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 özelliğiyle işaretlenen bir dizi Trusty hizmeti, güvenli olmayan tarafta çalışan çekirdek ve kullanıcı alanı programları tarafından erişilebilir.

Güvenli olmayan taraftaki (çekirdek ve kullanıcı alanı) yürütme ortamı, güvenli taraftaki yürütme ortamından önemli ölçüde farklıdır. Bu nedenle, her iki ortam için de tek bir kitaplık yerine iki farklı API grubu vardır. Çekirdekte, istemci API'si trusty-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 cihazı düğümü kaydeder.

Kullanıcı alanı Trusty IPC Client API

Kullanıcı alanı Trusty IPC Client API kitaplığı, fd cihaz düğümünün üstündeki ince bir katmandır.

Kullanıcı alanı programı, tipc_connect() çağrısı yaparak bir iletişim oturumu başlatır ve belirtilen Trusty hizmetine bağlantı oluşturur. Dahili olarak tipc_connect() çağrısı, dosya tanımlayıcısını almak için belirtilen bir cihaz düğümünü açar ve bağlanılacak hizmet adını içeren bir dizeyi işaret eden argp parametresini içeren bir TIPC_IOC_CONNECT ioctl() çağrısı başlatır.

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

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

tipc_connect() çağrısı tarafından elde edilen dosya tanımlayıcısı, tipik bir karakter cihazı düğümü gibi davranır. Dosya tanımlayıcısı:

  • Gerekirse engellemeyen moda geçirilebilir
  • Diğer tarafa mesaj göndermek için standart bir write() çağrısı kullanılarak yazılabilir
  • Normal bir dosya tanımlayıcısı olarak gelen iletilerin kullanılabilirliği için anket yapılabilir (poll() çağrıları veya select() çağrıları kullanılarak)
  • Gelen mesajları almak için okunabilir

Bir arayanın, belirtilen fd için yazma çağrısı gerçekleştirerek Trusty hizmetine mesaj göndermesi. Yukarıdaki write() çağrısına iletilen tüm veriler, trusty-ipc sürücüsü tarafından bir mesaja dönüştürülür. Mesaj, verilerin Trusty çekirdeğinde IPC alt sistemi tarafından işlendiği ve uygun hedefe yönlendirildiği ve belirli bir kanal tutamacında IPC_HANDLE_POLL_MSG etkinliği olarak bir uygulama etkinlik döngüsüne aktarıldığı güvenli tarafa gönderilir. Trusty hizmeti, hizmete özel protokole bağlı olarak bir veya daha fazla yanıt mesajı gönderebilir. Bu mesajlar, güvenli olmayan tarafa geri gönderilir ve kullanıcı alanı uygulaması read() çağrısı tarafından alınabilmesi için uygun kanal dosya tanımlayıcısı mesaj kuyruğuna yerleştirilir.

tipc_connect()

Belirli bir tipc cihaz düğümünü açar ve belirli bir Trusty hizmetine bağlantı başlatır.

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

[in] dev_name: Açılacak Trusty IPC cihaz düğümünün yolu

[in] srv_name: Bağlanılacak yayınlanmış Trusty hizmetinin adı

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

tipc_close()

Bir dosya tanımlayıcısıyla belirtilen Trusty hizmetine olan bağlantıyı kapatır.

int tipc_close(int fd);

[in] fd: Daha önce bir tipc_connect() çağrısıyla açılmış dosya tanımlayıcı

Kernel Trusty IPC Client API

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

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

Bağlantının başarıyla kurulduğuna dair bildirim (handle_event() geri çağırma aracılığıyla) aldıktan sonra arayan 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
  • İletiyi, kanalın bağlı olduğu güvenilir bir hizmete (güvenli tarafta) teslim etmek için tipc_chan_queue_msg() yöntemini kullanarak sıraya ekler

Mesaj arabelleği, uzak taraftan işlendikten sonra (daha sonra yeniden kullanılmak üzere, diğer mesajlar için) ücretsiz arabellek havuzuna geri döneceğinden, mesaj arabelleği sıraya eklendikten sonra arayan tarafından unutulmalıdır. Kullanıcının yalnızca bu tür bir arabelleği sıraya ekleyememesi veya artık gerekli olmaması durumunda tipc_chan_put_txbuf() işlevini çağırması gerekir.

API kullanıcısı, işlenecek bir gelen mesaj içeren rx arabelleğinin işaretçisini sağlayan handle_msg() bildirim geri çağırma işlevini (trusty-ipc rx iş havuzu bağlamında çağrılır) işleyerek uzak taraftan mesaj alır.

handle_msg() geri çağırma işlevinin geçerli bir struct tipc_msg_buf işaretçisi döndürmesi beklenir. Yerel olarak ele alınıp artık gerekli değilse gelen mesaj arabelleği ile aynı olabilir. Alternatif olarak, gelen arabellek daha fazla işlem için sıraya alınırsa tipc_chan_get_rxbuf() çağrısı ile elde edilen yeni bir arabellek olabilir. Ayrıştırılmış bir rx arabelleği izlenmeli ve artık gerekli olmadığında tipc_chan_put_rxbuf() çağrısı kullanılarak serbest bırakılmalıdır.

Kernel Trusty IPC İstemci API'sindeki yöntemler

tipc_create_channel()

Belirli bir trusty-ipc cihazı için Trusty 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);

[in] dev: Cihaz kanalının oluşturulduğu trusty-ipc işaretçisi

[in] ops: Arayana özgü geri çağırma işlevlerinin doldurulduğu bir struct tipc_chan_ops işaretçisi

[in] cb_arg: tipc_chan_ops geri aramalarına iletilen verilerin işaretçisi

[retval]: Başarılı olduğunda yeni oluşturulmuş bir struct tipc_chan örneğinin, aksi takdirde ERR_PTR(err) işaretçisi

Genel olarak, bir araya çağıran, ilgili etkinlik gerçekleşirken eşzamansız olarak çağrılan iki geri çağırma sağlamalıdır.

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

[in] cb_arg: tipc_create_channel() çağrısına iletilen verilerin işaretçisi

[in] event: Aşağıdaki değerlerden biri olabilecek bir etkinlik:

  • TIPC_CHANNEL_CONNECTED: Uzak tarafa başarılı bir bağlantı olduğunu gösterir
  • TIPC_CHANNEL_DISCONNECTED: Uzak tarafın yeni bağlantı isteğini reddettiğini veya daha önce bağlı olan kanalın bağlantısının kesilmesini istediğini gösterir.
  • TIPC_CHANNEL_SHUTDOWN: Uzak tarafın kapandığını ve tüm bağlantıların kalıcı olarak sonlandırıldığını gösterir

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

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

tipc_chan_connect()

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

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

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

[in] port: Bağlanılacak hizmet adını içeren bir dizenin işaretçisi

[retval]: Başarılı olursa 0, aksi takdirde negatif bir hata

Bağlantı kurulduğunda arayana handle_event geri arama yapılarak bildirim gönderilir.

tipc_chan_shutdown()

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

int tipc_chan_shutdown(struct tipc_chan *chan);

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

tipc_chan_destroy()

Belirtilen Trusty IPC kanalını yok eder.

void tipc_chan_destroy(struct tipc_chan *chan);

[in] 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 elde eder. Arabellek hemen kullanılamıyorsa arayan, belirtilen zaman aşımı (milisaniye cinsinden) boyunca engellenebilir.

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

[in] chan: İletinin sıraya ekleneceği kanalın işaretçisi

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

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

tipc_chan_queue_msg()

Belirtilen Trusty IPC kanalları üzerinden gönderilecek bir iletiyi sıraya ekler.

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

[in] chan: İletiyi sıraya ekleyeceğiniz kanalın işaretçisi

[in] mb: Sıraya eklenecek iletiye işaretçi (tipc_chan_get_txbuf_timeout() çağrısı ile elde edilir)

[retval]: Başarılı olursa 0, aksi takdirde negatif bir hata

tipc_chan_put_txbuf()

Daha önce bir 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);

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

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

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

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

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

tipc_chan_put_rxbuf()

Daha önce bir 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);

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

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

[retval]: Yok