Documentation de référence de l'API Trusty

Trusty fournit des API permettant de développer deux classes d'applications/services:

  • Applications ou services de confiance s'exécutant sur le processeur TEE
  • Applications normales/non fiables qui s'exécutent sur le processeur principal et utilisent les services fournis par des applications de confiance

Le Trusty L'API décrit généralement le système de communication inter-processus (IPC) Trusty, y compris les communications avec le monde non sécurisé. Les logiciels exécutés sur processeur principal peut utiliser les API Trusty pour se connecter à des applications/services de confiance et échanger des messages arbitraires avec eux comme un service réseau via IP. Il appartient à l'application de déterminer le format des données et la sémantique de ces à l'aide d'un protocole au niveau de l'application. Une distribution fiable des messages est garanti par l’infrastructure Trusty sous-jacente (sous la forme de pilotes exécuté sur le processeur principal) et la communication est complètement asynchrone.

Ports et canaux

Les ports sont utilisés par les applications Trusty pour exposer les points de terminaison des services sous la forme d'un chemin nommé auquel les clients se connectent. Cela donne un modèle simple, basé sur des chaînes pour les clients. La convention d'attribution de noms suit le DNS inversé par exemple, com.google.servicename

Lorsqu'un client se connecte à un port, il reçoit un canal pour interagir avec un service. Le service doit accepter une connexion entrante et lorsqu'il il reçoit également un canal. En substance, les ports sont utilisés pour rechercher des services puis la communication a lieu sur une paire de canaux connectés (c'est-à-dire instances de connexion sur un port). Lorsqu'un client se connecte à un port, une liaison symétrique, une connexion bidirectionnelle est établie. Grâce à ce chemin full-duplex, les clients et les serveurs peuvent échanger des messages arbitraires jusqu'à ce que chaque partie la connexion.

Seules les applications approuvées côté sécurisé ou les modules du noyau Trusty peuvent créer . Les applications exécutées sur un côté non sécurisé (en environnement normal) peuvent ne se connectent qu'aux services publiés par le côté sécurisé.

Selon les exigences, une application de confiance peut être à la fois un client serveur en même temps. Il s'agit d'une application de confiance qui publie un service (en tant que serveur) peuvent avoir besoin de se connecter à d'autres services (en tant que client).

Gérer l'API

Les identifiants sont des entiers non signés représentant des ressources telles que des ports et canaux, comme les descripteurs de fichiers dans UNIX. Une fois les identifiants créés, sont placés dans une table de handle spécifique à l'application et peuvent être référencés plus tard.

Un appelant peut associer des données privées à un identifiant en utilisant la méthode set_cookie().

Méthodes de l'API Handle

Les identifiants ne sont valides que dans le contexte d'une application. Une application doit ne pas transmettre la valeur d'un identifiant à d'autres applications, sauf si explicitement spécifié. Une valeur de handle uniquement doit être interprétée en la comparant avec le INVALID_IPC_HANDLE #define, qu'une application peut utiliser comme indique qu'un handle est non valide ou non défini.

Associe les données privées fournies par l'appelant à un identifiant spécifié.

long set_cookie(uint32_t handle, void *cookie)

[dans] handle: tout handle renvoyé par l'un des appels d'API

[dans] cookie: pointeur vers des données d'espace utilisateur arbitraires dans l'application Trusty

[retval]: NO_ERROR en cas de réussite, code d'erreur < 0 dans le cas contraire

Cet appel est utile pour gérer les événements lorsqu'ils se produisent ultérieurement l'identifiant a été créé. Le mécanisme de gestion des événements fournit le handle et son cookie au gestionnaire d'événements.

Les identifiants peuvent être attendus pour les événements à l'aide de l'appel wait().

attente()

Attend qu'un événement se produise sur un identifiant donné pendant une période spécifiée.

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

[dans] handle_id: tout handle renvoyé par l'un des appels d'API

[out] event: pointeur vers la structure représentant un événement qui s'est produit sur cet identifiant

[in] timeout_msecs: valeur du délai avant expiration, en millisecondes. un La valeur -1 correspond à un délai avant expiration infini

[retval]: NO_ERROR si un événement valide s'est produit au cours d'une délai avant expiration spécifié ; ERR_TIMED_OUT si un délai d'inactivité spécifié s'est écoulé, mais qu'aucune s'est produit. < 0 pour les autres erreurs

En cas de réussite (retval == NO_ERROR), l'appel wait() remplit une structure uevent_t spécifiée avec des informations sur l'événement qui s'est produit.

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;

Le champ event contient une combinaison des valeurs suivantes:

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 : aucun événement n'est en attente, l'appelant doit relancer l'attente

IPC_HANDLE_POLL_ERROR : une erreur interne non spécifiée s'est produite

IPC_HANDLE_POLL_READY : dépend du type d'identifiant, comme suit :

  • Pour les ports, cette valeur indique qu'une connexion est en attente
  • Pour les canaux, cette valeur indique qu'une connexion asynchrone (voir connect()) a été établi

Les événements suivants ne concernent que les chaînes:

  • IPC_HANDLE_POLL_HUP : une chaîne a été clôturée par un pair.
  • IPC_HANDLE_POLL_MSG : indique qu'un message est en attente pour ce canal
  • IPC_HANDLE_POLL_SEND_UNBLOCKED : indique qu'une l'appelant bloqué peut tenter d'envoyer un message à nouveau (pour en savoir plus, consultez la description de send_msg()).

Un gestionnaire d'événements doit être prêt à gérer une combinaison des car plusieurs bits peuvent être définis en même temps. Par exemple, pour un d'un canal de distribution, il est possible qu'il y ait des messages en attente et qu'une connexion vos pairs en même temps.

La plupart des événements sont persistants. Elles persistent tant que la condition sous-jacente persistance (par exemple, tous les messages en attente sont reçus et en attente de connexion) ; sont traitées). La seule exception est le cas l'événement IPC_HANDLE_POLL_SEND_UNBLOCKED, qui est effacé lors d'une lecture, et l'application n'a qu'une seule chance de le gérer.

Les identifiants peuvent être détruits en appelant la méthode close().

fermer()

Détruit la ressource associée au handle spécifié et la supprime de le tableau de identifiant.

long close(uint32_t handle_id);

[in] handle_id: gérer pour détruire

[retval]: 0 en cas de réussite ; une erreur négative, sinon

API serveur

Un serveur commence par créer un ou plusieurs ports nommés représentant et ses points de terminaison de service. Chaque port est représenté par un handle.

Méthodes de l'API Server

port_create()

Crée un port de service nommé.

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

[in] path: nom de chaîne du port (comme décrit ci-dessus). Ce doit être unique dans l'ensemble du système, les tentatives de création de doublons échoueront.

[in] num_recv_bufs: nombre maximal de tampons dans lesquels une chaîne ce port peut pré-allouer pour faciliter l'échange de données avec le client. Décompte des tampons séparément pour les données allant dans les deux sens. Par conséquent, spécifier 1 ici signifierait 1 d'envoi et 1 tampon de réception sont pré-alloués. En général, le nombre de tampons requise dépend du protocole de niveau supérieur conclu entre le client et Google Cloud. Le nombre peut être égal à 1 dans le cas d'un protocole très synchrone. (envoyer un message, recevoir une réponse avant d'en envoyer un autre). Mais ce nombre peut être plus d'un message si le client s'attend à envoyer plusieurs messages avant qu'une réponse ne puisse (un message en tant que prologue et un autre en tant que commande). La les ensembles de tampons alloués sont par canal, de sorte que deux connexions distinctes (canaux) disposeraient d'ensembles de tampons distincts.

[in] recv_buf_size: taille maximale de chaque tampon individuel dans au-dessus du tampon défini. Cette valeur est dépend du protocole et limite efficacement la taille maximale des messages que vous pouvez échanger avec un pair

[dans] flags: combinaison d'options spécifiant un comportement supplémentaire pour le port.

Cette valeur doit être une combinaison des valeurs suivantes:

IPC_PORT_ALLOW_TA_CONNECT : autorise une connexion depuis d'autres applications sécurisées

IPC_PORT_ALLOW_NS_CONNECT : autorise une connexion depuis un environnement non sécurisé.

[retval]: gestion du port créé s'il s'agit d'une valeur non négative ou d'une erreur spécifique si négative

Le serveur interroge ensuite la liste des poignées de port pour identifier les connexions entrantes. via l'appel wait(). À la réception d'une connexion indiquée par le bit IPC_HANDLE_POLL_READY défini dans le champ event de la structure uevent_t, le serveur doit appeler accept() pour terminer l'établissement d'une connexion et créer un canal (représenté par un autre identifiant) qui peut ensuite être interrogé pour les messages entrants.

accepter()

Accepte une connexion entrante et obtient un identifiant pour un canal.

long accept(uint32_t handle_id, uuid_t *peer_uuid);

[in] handle_id: handle représentant le port auquel un client s'est connecté

[out] peer_uuid: pointeur vers une structure uuid_t à rempli par l'UUID de l'application cliente connectée. Il est défini sur tous les zéros si la connexion provient d'un monde non sécurisé

[retval]: traite vers un canal (s'il n'est pas négatif) sur lequel le serveur peut échanger des messages avec le client (ou un code d'erreur dans le cas contraire) ;

API cliente

Cette section présente les méthodes de l'API cliente.

Méthodes de l'API cliente

se connecter()

Lance une connexion à un port indiqué par son nom.

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

[dans] path: nom d'un port publié par une application Trusty

[in] flags: spécifie un comportement supplémentaire facultatif

[retval]: redirige vers un canal via lequel les messages peuvent être échangés avec serveur ; erreur si négative

Si aucun flags n'est spécifié (le paramètre flags est définie sur 0), l'appel de connect() initie une connexion synchrone. vers un port spécifié qui renvoie une erreur si le port n'existe pas et crée un bloc jusqu'à ce que le accepte une connexion.

Ce comportement peut être modifié en spécifiant une combinaison de deux valeurs, décrits ci-dessous:

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

IPC_CONNECT_WAIT_FOR_PORT : force un connect(). appeler pour attendre si le port spécifié n'existe pas immédiatement au moment de l'exécution, au lieu d'échouer immédiatement.

IPC_CONNECT_ASYNC : s'il est défini, initie une connexion asynchrone. Une l'application doit interroger le handle renvoyé (en appelant wait() pour un événement de fin de connexion indiqué par le IPC_HANDLE_POLL_READY bit défini dans le champ d'événement de la structure uevent_t avant de commencer fonctionnement normal.

API Messaging

Les appels de l'API Messaging permettent d'envoyer et de lire des messages via une une connexion précédemment établie (canal). Les appels de l'API Messaging sont même pour les serveurs et les clients.

Un client reçoit un handle vers un canal en émettant un connect(). et qu'un serveur obtient un identifiant de canal à partir d'un appel accept(), décrites ci-dessus.

Structure d'un message Trusty

Comme indiqué ci-dessous, les messages échangés par l'API Trusty ont un minimum la structure, ce qui laisse le serveur et le client s'accorder sur la sémantique contenu réel:

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

Un message peut être composé d'un ou de plusieurs tampons non contigus représentés par Un tableau de structures iovec_t Trusty effectue des activités de dispersion lit et écrit dans ces blocs à l'aide du tableau iov. Contenu des tampons pouvant être décrits par le tableau iov est entièrement arbitraire.

Méthodes de l'API Messaging

envoyer_msg()

Envoie un message sur un canal spécifié.

long send_msg(uint32_t handle, ipc_msg_t *msg);

[dans] handle: identifiant de la chaîne sur laquelle envoyer le message

[in] msg: pointeur vers l'élément ipc_msg_t structure décrivant le message

[retval]: nombre total d'octets envoyés en cas de réussite. une erreur négative, sinon

Si le client (ou le serveur) tente d'envoyer un message via le canal et s'il n'y a pas d'espace dans la file d'attente des messages du pair de destination, le canal peut passer à un état de blocage d'envoi (cela ne doit jamais se produire dans le cas d'une règle protocole de requête/réponse, mais cela peut se produire dans des cas plus complexes), soit indiqué en renvoyant un code d'erreur ERR_NOT_ENOUGH_BUFFER. Dans ce cas, l'appelant doit attendre que le pair libère une partie dans sa file d'attente de réception en récupérant le traitement et la suppression des messages, indiquée par le bit IPC_HANDLE_POLL_SEND_UNBLOCKED défini dans Le champ event de la structure uevent_t renvoyé par l'appel wait().

get_msg()

Récupère les méta-informations sur le message suivant dans une file d'attente de messages entrants.

d'un critère spécifié.

long get_msg(uint32_t handle, ipc_msg_info_t *msg_info);

[dans] handle: identifiant du canal sur lequel un nouveau message doit être récupéré

[out] msg_info: structure d'informations du message décrite comme suit:

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

Chaque message reçoit un identifiant unique parmi les messages en attente. et la longueur totale de chaque message est renseignée. Si la configuration et l'autorisation du plusieurs messages en attente (ouverts) à la fois un canal particulier.

[retval]: NO_ERROR en cas de réussite une erreur négative, sinon

read_msg()

Lit le contenu du message ayant l'ID spécifié à partir de la un décalage spécifié.

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

[dans] handle: identifiant de la chaîne depuis laquelle lire le message

[dans] msg_id: ID du message à lire

[dans] offset: décalage par rapport au message à partir duquel commencer la lecture

[out] msg: pointeur vers la structure ipc_msg_t décrivant Un ensemble de tampons dans lequel stocker les messages entrants données

[retval]: nombre total d'octets stockés dans les tampons msg sur succès ; une erreur négative, sinon

La méthode read_msg peut être appelée plusieurs fois à partir de est différent (pas nécessairement séquentiel) au besoin.

mettre_msg()

Supprime un message dont l'ID est spécifié.

long put_msg(uint32_t handle, uint32_t msg_id);

[dans] handle: identifiant de la chaîne sur laquelle le message est arrivé

[dans] msg_id: ID du message en cours de suppression

[retval]: NO_ERROR en cas de réussite une erreur négative, sinon

Impossible d'accéder au contenu d'un message après sa suppression et qu'il occupait a été libérée.

API File Descriptor

L'API File Descriptor inclut read(), write(), et ioctl(). Tous ces appels peuvent s'exécuter sur un ensemble prédéfini (statique) de fichiers descripteurs traditionnellement représentés par de petits nombres. Dans la version actuelle l'implémentation, l'espace de descripteur de fichier est distinct du handle IPC l'espace de stockage. L'API File Descriptor dans Trusty est semblable à une API traditionnelle basée sur des descripteurs de fichier.

Par défaut, il existe trois descripteurs de fichier prédéfinis (standards et connus) :

  • 0 : entrée standard. L'implémentation par défaut de l'entrée standard fd est une opération no-op (les applications de confiance ne devant pas nécessairement disposer d'un environnement console). Ainsi, la lecture, l'écriture ou l'appel de ioctl() sur fd 0 doit renvoyer une erreur ERR_NOT_SUPPORTED.
  • 1 : sortie standard. Les données écrites sur la sortie standard peuvent être acheminées (en fonction au niveau de débogage LK) vers UART et/ou un journal de mémoire disponible sur le selon la plate-forme et la configuration. Les journaux de débogage non critiques les messages devraient apparaître dans la sortie standard. read() et ioctl() ne sont pas utilisées et doivent renvoyer une erreur ERR_NOT_SUPPORTED.
  • 2 : erreur standard. Les données écrites sur l'erreur standard doivent être acheminées vers l'UART ou le journal de la mémoire disponible du côté non sécurisé, en fonction de la plateforme et configuration. Il est recommandé d'écrire uniquement les messages critiques dans les car il est très probable que ce flux ne soit pas limité. Les read() et Les méthodes ioctl() sont no-ops et doivent renvoyer une erreur ERR_NOT_SUPPORTED.

Même si cet ensemble de descripteurs de fichier peut être étendu pour implémenter fds (pour implémenter des extensions spécifiques à la plate-forme), extension des descripteurs de fichier à utiliser avec précaution. L'extension des descripteurs de fichier entraîne souvent des conflits et n'est généralement pas recommandée.

Méthodes de l'API File Descriptor

lire()

Tente de lire jusqu'à count octets de données à partir d'un descripteur de fichier spécifié.

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

[dans] fd: descripteur de fichier à partir duquel la lecture

[out] buf: pointeur vers un tampon dans lequel stocker des données

[in] count: nombre maximal d'octets à lire

[retval]: nombre d'octets lus renvoyés. une erreur négative, sinon

écriture()

Écrit jusqu'à count octets de données dans le descripteur de fichier spécifié.

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

[dans] fd: descripteur de fichier dans lequel écrire

[out] buf: pointeur vers les données à écrire

[in] count: nombre maximal d'octets à écrire

[retval]: nombre d'octets écrits renvoyés. une erreur négative, sinon

ioctl()

Invoque une commande ioctl spécifiée pour un descripteur de fichier donné.

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

[dans] fd: descripteur de fichier sur lequel appeler ioctl()

[dans] cmd: commande ioctl

[entrée/sortie] args: pointeur vers ioctl() arguments

API Miscellaneous

Méthodes de l'API Miscellaneous

gettime()

Renvoie l'heure système actuelle (en nanosecondes).

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

[in] clock_id: dépendant de la plate-forme ; transmettre zéro pour la valeur par défaut

[in] flags: réservé, doit être égal à zéro

[out] time: pointeur vers une valeur int64_t où stocker l'heure actuelle

[retval]: NO_ERROR en cas de réussite une erreur négative, sinon

nanosleep()

Suspend l'exécution de l'application appelante pendant une période spécifiée et le réactive après cette période.

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

[in] clock_id: réservé, doit être égal à zéro

[in] flags: réservé, doit être égal à zéro

[in] sleep_time: délai de mise en veille en nanosecondes

[retval]: NO_ERROR en cas de réussite une erreur négative, sinon

Exemple de serveur d'application de confiance

L'exemple d'application suivant illustre l'utilisation des API ci-dessus. L'exemple crée un "écho" qui gère plusieurs connexions entrantes et renvoie à l'appelant tous les messages qu'il reçoit des clients. du côté sécurisé ou non.

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

La méthode run_end_to_end_msg_test() envoie 10 000 messages de manière asynchrone. jusqu'à "echo" et gère réponses.

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

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

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

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

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

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

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

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

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

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

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

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

abort_test:
  close(chan);
  return rc;
}

API et applications non sécurisées

Un ensemble de services Trusty, publiés du côté sécurisé et marqués de la mention l'attribut IPC_PORT_ALLOW_NS_CONNECT, sont accessibles au noyau et les programmes d’espace utilisateur exécutés sur le non sécurisé.

L'environnement d'exécution du côté non sécurisé (noyau et espace utilisateur) radicalement différent de l'environnement d'exécution du côté sécurisé. Par conséquent, au lieu d'utiliser une seule bibliothèque pour les deux environnements, différents ensembles d'API. Dans le noyau, l'API cliente est fournie par le le pilote du noyau trusty-ipc et enregistre un nœud de périphérique de caractères pouvant être utilisé par les processus de l’espace utilisateur pour communiquer avec les services exécutés sur le sur le côté.

API Trusty IPC Client de l'espace utilisateur

La bibliothèque d'API Trusty IPC Client de l'espace utilisateur est une couche fine située au-dessus du nœud d'appareil fd.

Un programme d’espace utilisateur démarre une session de communication en appelant tipc_connect(), pour initialiser une connexion à un service Trusty spécifié. En interne, l'appel tipc_connect() ouvre un nœud d'appareil spécifié obtenir un descripteur de fichier et appelle une TIPC_IOC_CONNECT ioctl() appel avec le paramètre argp pointant vers une chaîne contenant un le nom du service auquel se connecter.

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

Le descripteur de fichier obtenu ne peut être utilisé que pour communiquer avec le service pour lequel il a été créé. Le descripteur de fichier doit être fermé en Appeler tipc_close() lorsque la connexion n'est plus nécessaire.

Le descripteur de fichier obtenu par l'appel tipc_connect() se comporte comme un nœud de périphérique de caractères typique ; le descripteur de fichier:

  • Possibilité de passer en mode non bloquant si nécessaire
  • Peut être écrite sur un write() standard appeler pour envoyer des messages à l'autre côté
  • Sondage possible (appels poll() ou select()) pour la disponibilité des messages entrants en tant que descripteur de fichier standard
  • Peut être lu pour récupérer les messages entrants

Un appelant envoie un message au service Trusty en exécutant un appel en écriture pour le fd spécifié. Toutes les données transmises à l'appel write() ci-dessus est transformé en message par le pilote trusty-ipc. Le message est livré du côté sécurisé où les données sont traitées par le sous-système IPC dans le noyau Trusty et acheminé vers la bonne destination et livré à une application boucle d'événements en tant qu'événement IPC_HANDLE_POLL_MSG sur un canal particulier identifiant. Selon le cas, protocole spécifique au service, le service Trusty peut envoyer une ou plusieurs réponses les messages qui sont renvoyés vers le côté non sécurisé et placés dans file d'attente des messages de descripteur de fichier de canal appropriée à récupérer par l'utilisateur l'appel read() de l'application spatiale.

tipc_connect() (en anglais)

Ouvre un nœud d'appareil tipc spécifié et lance une à un service Trusty spécifié.

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

[in] dev_name: chemin d'accès au nœud d'appareil IPC Trusty à ouvrir

[dans] srv_name: nom d'un service Trusty publié auquel se connecter

[retval]: descripteur de fichier valide en cas de réussite, -1 dans les autres cas.

tipc_close()

Ferme la connexion au service Trusty spécifié par un descripteur de fichier.

int tipc_close(int fd);

[in] fd: descripteur de fichier précédemment ouvert par un appel tipc_connect()

API Noyau Trusty IPC Client

L’API client Trusty IPC du noyau est disponible pour les pilotes du noyau. L'utilisateur L'API Space Trusty IPC est implémentée sur cette API.

En général, cette API est utilisée en général par un appelant qui crée un objet struct tipc_chan à l'aide de tipc_create_channel() puis en utilisant l'appel tipc_chan_connect() pour initier une au service Trusty IPC qui s'exécute sur le réseau sur le côté. La connexion côté distant peut être interrompue en appelant tipc_chan_shutdown(), suivi de tipc_chan_destroy() pour nettoyer les ressources.

À la réception d'une notification (via le rappel handle_event()) qu'une connexion a bien été établie, un appelant les éléments suivants:

  • Récupère un tampon de message à l'aide de l'appel tipc_chan_get_txbuf_timeout().
  • rédige un message ;
  • Met le message en file d'attente à l'aide de tipc_chan_queue_msg() mode de livraison à un service Trusty (du côté sécurisé), auquel le chaîne est associée

Une fois la mise en file d'attente réussie, l'appelant doit oublier le tampon du message car le tampon de message revient finalement dans le pool de mémoire tampon libre par le côté distant (pour les réutiliser ultérieurement, pour d'autres messages). L'utilisateur n'a besoin d'appeler tipc_chan_put_txbuf() qu'en cas d'échec un tel tampon, ou il n'est plus nécessaire.

Un utilisateur de l'API reçoit des messages du côté distant en gérant une Rappel de notification handle_msg() (appelé dans le contexte de la file d'attente rx de confiance-ipc) que fournit un pointeur vers un tampon rx contenant un à traiter un message entrant.

Le rappel handle_msg() doit normalement renverra un pointeur vers un struct tipc_msg_buf valide. Il peut être identique au tampon de messages entrants s'il est géré localement. et ne sont plus obligatoires. Il peut également s'agir d'un nouveau tampon obtenu par Un appel tipc_chan_get_rxbuf() si le tampon entrant est mis en file d'attente pour un traitement ultérieur. Un tampon rx dissocié doit être suivi avant de les publier à l'aide d'un appel tipc_chan_put_rxbuf() il n'est plus nécessaire.

Méthodes de l'API cliente Kernel Trusty IPC

tipc_create_channel()

Crée et configure une instance d'un canal d'IPC fiable pour un canal particulier fiable-ipc.

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

[in] dev: pointeur vers l'adresse IP fiable pour laquelle l'appareil chaîne est créée

[dans] ops: pointeur vers un struct tipc_chan_ops. spécifiques à l'appelant rappels renseignés

[dans] cb_arg: pointeur vers les données qui seront transmises aux rappels tipc_chan_ops

[retval]: pointeur vers une nouvelle instance de struct tipc_chan en cas de réussite, ERR_PTR(err) dans les autres cas

En général, un appelant doit fournir deux rappels qui sont appelés de manière asynchrone. lorsque l'activité correspondante a lieu.

L'événement void (*handle_event)(void *cb_arg, int event) est appelé pour informer un appelant d'un changement d'état d'un canal.

[in] cb_arg: pointeur vers des données transmises à un Appel tipc_create_channel()

[dans] event: événement pouvant correspondre à l'une des valeurs suivantes:

  • TIPC_CHANNEL_CONNECTED : indique que la connexion a réussi. du côté distant
  • TIPC_CHANNEL_DISCONNECTED : indique que le côté distant a refusé le nouvelle demande de connexion ou demande déconnexion du canal précédemment connecté
  • TIPC_CHANNEL_SHUTDOWN : indique que le côté distant est en cours d'arrêt. l'interruption définitive de toutes les connexions

struct tipc_msg_buf *(*handle_msg)(void *cb_arg, struct tipc_msg_buf *mb) est appelé pour fournir une notification qu'un nouveau message a été reçues sur un canal spécifié:

  • [in] cb_arg: pointeur vers les données transmises à la Appel tipc_create_channel()
  • [dans] mb: pointeur vers un struct tipc_msg_buf décrivant un message entrant
  • [retval]: l'implémentation du rappel doit renvoyer un pointeur vers une struct tipc_msg_buf, qui peut correspondre au même pointeur reçu en tant que Paramètre mb si le message est géré localement et n'est pas n'est plus nécessaire peut être un nouveau tampon obtenu par l'appel tipc_chan_get_rxbuf()).

tipc_chan_connect() (en anglais)

Lance une connexion au service Trusty IPC spécifié.

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

[in] chan: pointeur vers une chaîne renvoyée par le Appel tipc_create_chan()

[in] port: pointeur vers une chaîne contenant le nom du service auquel se connecter

[retval]: 0 en cas de réussite, une erreur négative dans le cas contraire

L'appelant est averti lorsqu'une connexion est établie en recevant une Rappel handle_event.

tipc_chan_shutdown()

Arrête une connexion au service Trusty IPC précédemment initiée par un appel tipc_chan_connect().

int tipc_chan_shutdown(struct tipc_chan *chan);

[dans] chan: pointeur vers une chaîne renvoyée par un appel tipc_create_chan()

tipc_chan_destroy()

Détruit un canal d'IPC fiable spécifié.

void tipc_chan_destroy(struct tipc_chan *chan);

[in] chan: pointeur vers une chaîne renvoyée par le Appel tipc_create_chan()

tipc_chan_get_txbuf_timeout()

Récupère un tampon de message qui peut être utilisé pour envoyer des données sur un canal. Si le tampon n'est pas immédiatement disponible, l'appelant risque d'être bloqué pendant la durée spécifiée (en millisecondes).

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

[dans] chan: pointeur vers la chaîne vers laquelle ajouter un message à la file d'attente

[in] chan: délai maximal d'attente jusqu'au Le tampon tx devient disponible

[retval]: tampon de message valide en cas de réussite ERR_PTR(err) en cas d'erreur

tipc_chan_queue_msg()

Met en file d'attente un message à envoyer via la Canaux d'IPC fiables.

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

[dans] chan: pointeur vers la chaîne sur laquelle placer le message en file d'attente.

[in] mb: Pointeur vers le message dans la file d'attente (obtenue par un appel tipc_chan_get_txbuf_timeout())

[retval]: 0 en cas de réussite, une erreur négative dans le cas contraire

tipc_chan_put_txbuf()

Libère le tampon de message Tx spécifié précédemment obtenue par un appel tipc_chan_get_txbuf_timeout().

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

[dans] chan: pointeur vers la chaîne de destination ce tampon de messages appartient

[in] mb: pointeur vers le tampon de messages à libérer

[retval]: aucun

tipc_chan_get_rxbuf()

Récupère un nouveau tampon de message qui peut être utilisé pour recevoir des messages via le le canal spécifié.

struct tipc_msg_buf *tipc_chan_get_rxbuf(struct tipc_chan *chan);

[in] chan: pointeur vers un canal auquel appartient ce tampon de messages

[retval]: tampon de message valide en cas de réussite, ERR_PTR(err) en cas d'erreur

tipc_chan_put_rxbuf()

Libère un tampon de message spécifié précédemment obtenu par une tipc_chan_get_rxbuf() appel.

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

[in] chan: pointeur vers un canal auquel appartient ce tampon de messages

[in] mb: pointeur vers un tampon de messages à libérer

[retval]: aucun