Trusty API-Referenz

Trusty bietet APIs für die Entwicklung von zwei Klassen von Anwendungen/Diensten:

  • Vertrauenswürdige Anwendungen oder Dienste, die auf dem TEE-Prozessor ausgeführt werden
  • Normale/nicht vertrauenswürdige Anwendungen, die auf dem Hauptprozessor ausgeführt werden und die bereitgestellten Dienste nutzen durch vertrauenswürdige Anwendungen

Das Trusty API beschreibt im Allgemeinen das IPC-System (Inter-Process-Kommunikation) von Trusty. auch in der unsicheren Welt. Software, die auf dem Hauptauftragsverarbeiter kann mithilfe von Trusty-APIs eine Verbindung zu vertrauenswürdigen Anwendungen/Diensten herstellen. und mit ihnen beliebige Nachrichten austauschen wie bei einem Netzwerkdienst über IP. Die Anwendung legt das Datenformat und die Semantik dieser Daten fest. Nachrichten mit einem Protokoll auf App-Ebene. Zuverlässige Zustellung von Nachrichten durch die zugrunde liegende Trusty-Infrastruktur (in Form von Treibern auf dem Hauptprozessor ausgeführt wird) und die Kommunikation vollständig asynchron ist.

Ports und Kanäle

Ports werden von Trusty-Anwendungen verwendet, um Dienstendpunkte im Formular verfügbar zu machen. eines benannten Pfades, zu dem Clients eine Verbindung herstellen. Damit erhalten Sie eine einfache, stringbasierte Dienst-ID, die Clients verwenden können. Die Namenskonvention ist Reverse DNS-Stil Benennung, z.B. com.google.servicename

Wenn ein Client eine Verbindung zu einem Port herstellt, empfängt der Client einen Kanal zur Interaktion mit einem Dienst. Der Dienst muss eine eingehende Verbindung akzeptieren erhält sie ebenfalls einen Kanal. Im Wesentlichen werden Ports verwendet, um Dienste nachzuschlagen. Die Kommunikation erfolgt über zwei verbundene Kanäle (d.h. an einem Port). Wenn ein Client eine Verbindung zu einem Port herstellt, wird ein symmetrischer eine bidirektionale Verbindung hergestellt wird. Über diesen Vollduplex-Pfad können Clients und Server können beliebige Nachrichten austauschen, bis eine Seite beschließt, die Daten die Verbindung unterbrechen.

Nur vertrauenswürdige Anwendungen auf Sicherheitsseite oder Trusty-Kernelmodule können Ports. Anwendungen, die auf der nicht sicheren Seite (normalerweise) ausgeführt werden, Stellen Sie nur Verbindungen zu Diensten her, die auf der sicheren Seite veröffentlicht wurden.

Je nach Anforderungen kann eine vertrauenswürdige Anwendung sowohl ein Client als auch ein auf dem Server. Eine vertrauenswürdige Anwendung, die einen Dienst veröffentlicht (als Server) möglicherweise eine Verbindung zu anderen Diensten (als Client) herstellen müssen.

API verarbeiten

Handles sind vorzeichenlose Ganzzahlen, die Ressourcen wie Ports und Kanäle, ähnlich wie Dateideskriptoren unter UNIX. Nachdem Aliasse erstellt wurden, werden in eine anwendungsspezifische Handle-Tabelle gestellt und können auf .

Ein Anrufer kann private Daten mit einem Alias verknüpfen, indem er die Methode set_cookie().

Methoden in der Handle API

Aliasse sind nur im Kontext einer Anwendung gültig. Eine Anwendung sollte Der Wert eines Handles wird nur dann an andere Anwendungen übergeben, wenn dies explizit angegeben ist. Ein Handle-Wert sollte nur durch einen Vergleich mit Den INVALID_IPC_HANDLE #define,, den eine Anwendung als Dies weist darauf hin, dass ein Alias ungültig oder nicht festgelegt ist.

Verbindet die vom Anrufer bereitgestellten privaten Daten mit einem bestimmten Alias.

long set_cookie(uint32_t handle, void *cookie)

[in] handle: Jeder Handle, der von einem der API-Aufrufe zurückgegeben wird

[in] cookie: Zeiger auf beliebige Nutzerdaten in der Trusty-Anwendung

[retval]: NO_ERROR bei Erfolg, andernfalls < 0-Fehlercode

Dieser Aufruf ist nützlich für die Verarbeitung von Ereignissen, wenn sie später auftreten der Alias wurde erstellt. Der Ereignisbehandlungsmechanismus stellt das Handle und das zugehörige Cookie an den Event-Handler zurück.

Mit dem wait()-Aufruf kann auf Aliasse für Ereignisse gewartet werden.

Warte()

Wartet, bis für einen bestimmten Zeitraum ein Ereignis bei einem bestimmten Alias eintritt.

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

[in] handle_id: Jeder Handle, der von einem der API-Aufrufe zurückgegeben wird

[out] event: ein Zeiger auf die Struktur, die darstellt ein Ereignis, das bei diesem Alias aufgetreten ist

[in] timeout_msecs: ein Wert für das Zeitlimit in Millisekunden; eine Der Wert -1 ist ein unbegrenztes Zeitlimit.

[retval]: NO_ERROR, wenn innerhalb eines angegebene Zeitüberschreitungsintervall ERR_TIMED_OUT, wenn ein angegebenes Zeitlimit überschritten wurde, aber keine Ereignis eingetreten ist; < 0 für andere Fehler

Bei Erfolg (retval == NO_ERROR) wird der wait()-Aufruf füllt eine angegebene uevent_t-Struktur mit Informationen über das Ereignis, das eingetreten ist.

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;

Das Feld event enthält eine Kombination der folgenden Werte:

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: Es stehen derzeit keine Ereignisse aus. Anrufer sollte die Warteschleife neu starten

IPC_HANDLE_POLL_ERROR: Ein nicht näher spezifizierter interner Fehler ist aufgetreten

IPC_HANDLE_POLL_READY – abhängig vom Aliastyp:

  • Bei Ports zeigt dieser Wert an, dass eine Verbindung aussteht
  • Bei Channels gibt dieser Wert an, dass eine asynchrone Verbindung (siehe connect()) wurde gegründet

Die folgenden Ereignisse sind nur für Kanäle relevant:

  • IPC_HANDLE_POLL_HUP: Gibt an, dass ein Kanal von einem Peer geschlossen wurde.
  • IPC_HANDLE_POLL_MSG: Gibt an, dass für diesen Kanal eine ausstehende Nachricht vorhanden ist.
  • IPC_HANDLE_POLL_SEND_UNBLOCKED gibt an, dass ein zuvor könnte ein Anrufer versuchen, Nachricht noch einmal senden (Details finden Sie in der Beschreibung von send_msg())

Ein Ereignis-Handler sollte so vorbereitet sein, dass eine Kombination aus angegebenen -Ereignisse, da mehrere Bits gleichzeitig festgelegt sein können. Beispiel: Für eine kann es vorkommen, dass ausstehende Nachrichten vorhanden sind und eine Verbindung durch einen Peer-to-Peer-Prinzip.

Die meisten Ereignisse sind fixiert. Sie bleiben bestehen, solange die zugrunde liegende bleibt bestehen (z. B. werden alle ausstehenden Nachrichten empfangen und die Verbindung steht aus) -Anfragen verarbeitet werden. Die einzige Ausnahme sind das Ereignis IPC_HANDLE_POLL_SEND_UNBLOCKED, das nach einem Lesevorgang gelöscht wird und die Anwendung damit klar wird.

Handles können durch Aufrufen der close()-Methode gelöscht werden.

Close()

Zerstört die mit dem angegebenen Alias verknüpfte Ressource und entfernt sie aus der Handle-Tabelle.

long close(uint32_t handle_id);

[in] handle_id: Alias zum Zerstören

[retval]: 0 if success; andernfalls einen negativen Fehler

Server-API

Ein Server beginnt mit der Erstellung eines oder mehrerer benannter Ports für ihre Dienstendpunkte. Jeder Port wird durch einen Handle dargestellt.

Methoden in der Server API

Port_create()

Erstellt einen benannten Dienstport.

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

[in] path: Der Stringname des Ports (wie oben beschrieben). Dieses Name sollte im gesamten System eindeutig sein. Duplikate zu erstellen.

[in] num_recv_bufs: Die maximale Anzahl von Zwischenspeichern, die für einen Kanal verwendet werden kann dieser Port vorab zugewiesen werden, um den Austausch von Daten mit dem Client zu erleichtern. Puffer werden gezählt separat für Daten, die in beide Richtungen reichen. Die Angabe von 1 würde also 1 ergeben. Sende- und 1 Empfangspuffer sind vorab zugewiesen. Im Allgemeinen ist die Anzahl der Puffer hängt von der übergeordneten Protokollvereinbarung zwischen Client und Server. Bei einem sehr synchronen Protokoll kann die Zahl nur 1 betragen. (Nachricht senden, Antwort erhalten, bevor eine neue gesendet wird) Die Zahl kann jedoch wenn der Client erwartet, mehr als eine Nachricht zu senden, bevor eine Antwort angezeigt werden (z. B. eine Nachricht als Prolog und eine andere als der eigentliche Befehl). Die Die zugewiesenen Puffersätze sind pro Kanal, sodass zwei separate Verbindungen (Kanäle) separate Puffersätze.

[in] recv_buf_size: Maximale Größe jedes einzelnen Zwischenspeichers in der über dem Puffer gesetzt. Dieser Wert ist protokollabhängig und schränkt effektiv die maximale Nachrichtengröße ein, die Sie austauschen können. mit Peer

[in] flags: eine Kombination von Flags, die zusätzliches Portverhalten angeben

Dieser Wert sollte eine Kombination der folgenden Werte sein:

IPC_PORT_ALLOW_TA_CONNECT: lässt eine Verbindung von anderen sicheren Apps zu

IPC_PORT_ALLOW_NS_CONNECT – lässt eine Verbindung aus der unsicheren Welt zu

[retval]: Handle für den erstellten Port, wenn nicht negativ, oder ein bestimmter Fehler, wenn negativ

Der Server fragt dann die Liste der Port-Handles für eingehende Verbindungen ab per wait()-Aufruf. Beim Empfang einer Verbindung Anfrage, die durch das Bit IPC_HANDLE_POLL_READY angegeben wird, das in das Feld event der uevent_t-Struktur, die Der Server sollte accept() aufrufen, um den Verbindungsaufbau abzuschließen und ein Channel (repräsentiert durch einen anderen Alias), der dann für eingehende Nachrichten abgerufen werden kann.

akzeptiere()

Akzeptiert eine eingehende Verbindung und erhält einen Alias für einen Kanal.

long accept(uint32_t handle_id, uuid_t *peer_uuid);

[in] handle_id: Handle, das den Port darstellt, mit dem ein Client eine Verbindung hergestellt hat

[out] peer_uuid: Zeiger auf eine uuid_t-Struktur, die ausgeführt werden soll mit der UUID der Clientanwendung, die die Verbindung herstellt. Es wird auf alle Nullen gesetzt, wenn die Verbindung aus der nicht sicheren Welt stammt

[retval]: Handle für einen Kanal (falls nicht negativ), auf dem der Server Nachrichten mit dem Kunden austauschen (oder andernfalls einen Fehlercode anzeigen)

Client-API

Dieser Abschnitt enthält die Methoden in der Client API.

Methoden in der Client API

Connect()

Initiiert eine Verbindung zu einem nament angegebenen Port.

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

[in] path: Name eines von einer Trusty-Anwendung veröffentlichten Ports

[in] flags: Gibt zusätzliches, optionales Verhalten an

[retval]: Alias für einen Kanal, über den Nachrichten mit dem Server; Fehler, wenn negativ

Wenn keine flags angegeben sind (Parameter flags) auf 0 gesetzt ist), wird durch den Aufruf von connect() eine synchrone Verbindung initiiert an einen bestimmten Port zu senden, gibt einen Fehler zurück, wenn der Anschluss nicht existiert, und erstellt einen Block, bis der akzeptiert sonst eine Verbindung.

Dieses Verhalten lässt sich ändern, indem Sie eine Kombination aus zwei Werten angeben: wie unten beschrieben:

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

IPC_CONNECT_WAIT_FOR_PORT – erzwingt connect() um zu warten, wenn der angegebene Port bei der Ausführung nicht sofort vorhanden ist. anstatt sofort zu scheitern.

IPC_CONNECT_ASYNC: Wenn dieser Wert festgelegt ist, wird eine asynchrone Verbindung initiiert. Eine Anwendung abfragen muss Den zurückgegebenen Handle (durch Aufrufen von wait() für ein Verbindungsabschlussereignis, das durch IPC_HANDLE_POLL_READY angegeben wird im Ereignisfeld der uevent_t-Struktur gesetzt, bevor Normalbetrieb.

Messaging-API

Die Messaging API-Aufrufe ermöglichen das Senden und Lesen von Nachrichten über eine zuvor hergestellte Verbindung (Kanal) Die Messaging API-Aufrufe sind die für Server und Clients.

Ein Client erhält einen Handle für einen Kanal, indem er ein connect() ausgibt. und ein Server erhält einen Kanal-Alias von einem accept()-Aufruf. beschrieben.

Struktur einer Trusty-Mitteilung

Wie unten gezeigt, haben die von der Trusty API ausgetauschten Nachrichten nur eine minimale und dem Client überlassen, sich auf die Semantik der Inhalte:

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

Eine Nachricht kann aus einem oder mehreren nicht zusammenhängenden Zwischenspeichern bestehen, die durch Array mit iovec_t-Strukturen Trusty führt Streu-Sammeln Lese- und Schreibvorgänge in diesen Blöcken, mithilfe des Arrays iov. Der Inhalt der Puffer, die beschrieben werden können iov völlig beliebig ist.

Methoden in der Messaging API

send_msg()

Sendet eine Nachricht über einen bestimmten Kanal.

long send_msg(uint32_t handle, ipc_msg_t *msg);

[in] handle: Alias für den Kanal, über den die Nachricht gesendet werden soll

[in] msg: Zeiger auf das ipc_msg_t structure, das die Nachricht beschreibt

[retval]: Gesamtzahl der bei Erfolg gesendeten Byte; andernfalls einen negativen Fehler

Wenn der Client (oder Server) versucht, eine Nachricht über den Kanal zu senden, in der Zielwarteschlange für Peer-Nachrichten nicht vorhanden ist, in den Status „Sende-blockiert“ ein. Dies sollte bei einer einfachen synchronen Anfrage-/Antwortprotokoll, was aber in komplizierteren Fällen passieren kann), angezeigt, indem der Fehlercode ERR_NOT_ENOUGH_BUFFER zurückgegeben wird. In diesem Fall muss der Aufrufer warten, bis der Peer einige in der Empfangswarteschlange ab, indem er die Nachrichten abruft, angezeigt durch das Bit IPC_HANDLE_POLL_SEND_UNBLOCKED, das in das Feld event der uevent_t-Struktur vom wait()-Aufruf zurückgegeben.

get_msg()

Ruft Metainformationen zur nächsten Nachricht in einer Warteschlange für eingehende Nachrichten ab.

eines bestimmten Channels.

long get_msg(uint32_t handle, ipc_msg_info_t *msg_info);

[in] handle: Handle des Kanals, über den eine neue Nachricht abgerufen werden muss

[out] msg_info: Struktur der Nachrichteninformationen wie folgt beschrieben:

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

Jeder Nachricht wird eine eindeutige ID in der Gruppe der ausstehenden Nachrichten zugewiesen. und die Gesamtlänge jeder Nachricht. Sofern konfiguriert und vom kann es mehrere ausstehende (geöffnete) Nachrichten gleichzeitig für einen für einen bestimmten Kanal.

[retval]: NO_ERROR bei Erfolg; andernfalls einen negativen Fehler

read_msg()

Liest den Inhalt der Nachricht mit der angegebenen ID ab dem angegebenen Offset.

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

[in] handle: Alias des Kanals, von dem die Nachricht gelesen werden soll

[in] msg_id: ID der zu lesenden Nachricht

[in] offset: Versatz in der Nachricht, ab der mit dem Lesen begonnen werden soll

[out] msg: Zeiger auf die ipc_msg_t-Struktur, die beschreibt Eine Reihe von Zwischenspeichern, in denen die eingehende Nachricht gespeichert werden soll. Daten

[retval]: Gesamtzahl der Byte, die in den msg-Zwischenspeichern gespeichert sind Erfolg; andernfalls einen negativen Fehler

Die Methode read_msg kann mehrmals aufgerufen werden, beginnend bei eine andere (nicht unbedingt sequenziellen Versatz.

put_msg()

Zieht eine Nachricht mit einer angegebenen ID zurück.

long put_msg(uint32_t handle, uint32_t msg_id);

[in] handle: Alias des Kanals, auf dem die Nachricht eingegangen ist

[in] msg_id: ID der Nachricht, die deaktiviert wird

[retval]: NO_ERROR bei Erfolg; andernfalls einen negativen Fehler

Der Zugriff auf den Nachrichteninhalt ist nicht mehr möglich, nachdem eine Nachricht entfernt wurde und der der von ihm belegte Zwischenspeicher freigegeben wurde.

File Descriptor API

Die File Descriptor API enthält read(), write(), und ioctl() Anrufe. Alle diese Aufrufe können mit einem vordefinierten (statischen) Satz von Dateien ausgeführt werden. Deskriptoren, die traditionell durch kleine Zahlen dargestellt werden. In der aktuellen -Implementierung ist der Dateideskriptor-Bereich vom IPC-Handle getrennt Leerzeichen. Die File Descriptor API in Trusty ist ähnlich wie eine herkömmliche API auf Basis von Dateideskriptoren.

Standardmäßig gibt es drei vordefinierte (Standard- und bekannte) Dateideskriptoren:

  • 0 – Standardeingabe. Die Standardimplementierung der Standardeingabe fd ist ein Null-Vorgang, da von vertrauenswürdigen Anwendungen erwartet wird, dass sie keine Console), sodass das Lesen, Schreiben oder Aufrufen von ioctl() auf fd 0 sollte den Fehler ERR_NOT_SUPPORTED zurückgeben.
  • 1 – Standardausgabe. In die Standardausgabe geschriebene Daten können auf LK-Debug-Ebene) in UART und/oder ein Speicherprotokoll, das auf der nicht sicheren je nach Plattform und Konfiguration. Nicht kritische Fehlerbehebungsprotokolle und Nachrichten in der Standardausgabe enthalten sein sollen. read() und ioctl() sind managementfreie Methoden und sollten den Fehler ERR_NOT_SUPPORTED zurückgeben.
  • 2 – Standardfehler. In Standardfehler geschriebene Daten sollten an die UART-Datei weitergeleitet werden oder Speicherprotokoll auf der nicht sicheren Seite, je nach Plattform Konfiguration. Es wird empfohlen, nur kritische Nachrichten in den Standardzugriff zu schreiben. da dieser Stream höchstwahrscheinlich nicht gedrosselt wird. Die read() und ioctl()-Methoden sind managementfrei und sollten den Fehler ERR_NOT_SUPPORTED zurückgeben.

Diese Dateideskriptoren können zwar erweitert werden, um weitere fds (um plattformspezifische Erweiterungen zu implementieren), wodurch die Anforderungen an Dateideskriptoren erweitert werden mit Vorsicht vorgehen. Das Erweitern von Dateideskriptoren ist anfällig für und wird generell nicht empfohlen.

Methoden in der File Descriptor API

read()

Versucht, bis zu count Byte an Daten aus einem angegebenen Dateideskriptor zu lesen.

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

[in] fd: Dateideskriptor, aus dem gelesen werden soll

[out] buf: Zeiger auf einen Zwischenspeicher, in dem Daten gespeichert werden sollen

[in] count: Maximale Anzahl der zu lesenden Byte

[retval]: Zurückgegebene Anzahl gelesener Byte; andernfalls einen negativen Fehler

write()

Schreibt bis zu count Byte an Daten in den angegebenen Dateideskriptor.

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

[in] fd: Dateideskriptor, in den geschrieben werden soll

[out] buf: Zeiger auf die zu schreibenden Daten

[in] count: Maximale Anzahl der zu schreibenden Byte

[retval]: Zurückgegebene Anzahl geschriebener Byte; andernfalls einen negativen Fehler

ioctl()

Ruft einen angegebenen ioctl-Befehl für einen bestimmten Dateideskriptor auf.

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

[in] fd: Dateideskriptor, für den ioctl() aufgerufen werden soll

[in] cmd: Der Befehl ioctl

[Ein/Aus] args: Zeiger auf ioctl()-Argumente

Miscellaneous API (Sonstiges API)

Methoden in der Miscellaneous API

gettime()

Gibt die aktuelle Systemzeit in Nanosekunden zurück.

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

[in] clock_id: plattformabhängig; Null für Standard übergeben

[in] flags: Reserviert, sollte null sein

[out] time: Zeiger auf einen int64_t-Wert, auf den die aktuelle Zeit gespeichert werden soll

[retval]: NO_ERROR bei Erfolg; andernfalls einen negativen Fehler

nanosleep()

Unterbricht die Ausführung der aufrufenden Anwendung für einen bestimmten Zeitraum und wird nach diesem Zeitraum fortgesetzt.

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

[in] clock_id: Reserviert, sollte null sein

[in] flags: Reserviert, sollte null sein

[in] sleep_time: Ruhemodus-Zeit in Nanosekunden

[retval]: NO_ERROR bei Erfolg; andernfalls einen negativen Fehler

Beispiel für einen vertrauenswürdigen Anwendungsserver

Die folgende Beispielanwendung zeigt die Verwendung der oben genannten APIs. Das Beispiel erzeugt ein „Echo“ ein Dienst, der mehrere eingehende Verbindungen und dem Aufrufer alle Nachrichten, die er von Clients erhält, der sicheren oder nicht sicheren Seite.

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

Die Methode run_end_to_end_msg_test() sendet 10.000 Nachrichten asynchron zum „Echo“ Dienst und Aliasse Antworten.

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

Unsichere APIs und Anwendungen

Eine Reihe von Trusty-Diensten, die auf der sicheren Seite veröffentlicht und mit das Attribut IPC_PORT_ALLOW_NS_CONNECT, sind für den Kernel zugänglich und Userspace-Programmen auf dem unsichere Seite.

Die Ausführungsumgebung auf der nicht sicheren Seite (Kernel und Userspace) ist auf der sicheren Seite erheblich von der Ausführungsumgebung unterscheidet. Daher gibt es statt einer Bibliothek für beide Umgebungen zwei verschiedene Gruppen von APIs. Im Kernel wird die Client API vom Trusty-ipc-Kerneltreiber und registriert einen Charaktergeräteknoten, der verwendet werden kann, von Prozessen im User-Space aus, um mit Diensten zu kommunizieren, die auf der sicheren zu verstehen.

Nutzerbereich Trusty IPC Client API

Die Trusty IPC Client API-Bibliothek des Nutzerbereichs ist eine dünne Schicht über dem Geräteknoten fd.

Ein Userspace-Programm startet eine Kommunikationssitzung durch Aufrufen von tipc_connect(), Initialisieren einer Verbindung zu einem angegebenen Trusty-Dienst. Intern Der tipc_connect()-Aufruf öffnet einen bestimmten Geräteknoten für ruft einen Dateideskriptor ab und ruft ein TIPC_IOC_CONNECT ioctl()-Element auf -Aufruf mit dem argp-Parameter, der auf einen String mit einem Dienstname, zu dem eine Verbindung hergestellt werden soll.

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

Der resultierende Dateideskriptor kann nur für die Kommunikation mit dem Dienst verwendet werden für die sie erstellt wurden. Der Dateideskriptor sollte durch tipc_close() wird angerufen, wenn die Verbindung nicht mehr benötigt wird.

Der Dateideskriptor, der durch den tipc_connect()-Aufruf abgerufen wurde verhält sich wie ein typischer Zeichengeräteknoten; Dateideskriptor:

  • Kann bei Bedarf in den nicht blockierenden Modus gewechselt werden
  • Kann mit einem standardmäßigen write() geschrieben werden Ruf an, um Nachrichten an die andere Seite zu senden
  • Kann abgefragt werden (über poll()- oder select()-Aufrufe) zur Verfügbarkeit eingehender Nachrichten als regulärer Dateideskriptor
  • Kann gelesen werden, um eingehende Nachrichten abzurufen

Ein Aufrufer sendet eine Nachricht an den Trusty-Dienst, indem er einen Schreibaufruf für die angegebene fd. Alle Daten, die an den obigen write()-Aufruf übergeben wurden wird vom Treiber „trusty-ipc“ in eine Nachricht umgewandelt. Die Nachricht lautet auf der sicheren Seite bereitgestellt, wo die Daten vom IPC-Subsystem dem Trusty-Kernel bereitgestellt und an das richtige Ziel weitergeleitet und an eine App gesendet. Ereignisschleife als IPC_HANDLE_POLL_MSG-Ereignis auf einem bestimmten Kanal Alias. Je nachdem, dienstspezifisches Protokoll verwendet, kann der Trusty-Dienst eine oder mehrere Antworten Nachrichten, die an die unsichere Seite zurückgesendet und im Ordner angemessene Kanaldatei-Deskriptor-Nachrichtenwarteschlange, die vom Nutzer abgerufen werden soll Aufruf der Space-App read().

tipc_connect()

Öffnet einen angegebenen tipc-Geräteknoten und initiiert einen Verbindung zu einem angegebenen Trusty-Dienst.

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

[in] dev_name: Pfad zum Trusty-IPC-Geräteknoten, der geöffnet werden soll

[in] srv_name: Name eines veröffentlichten Trusty-Dienstes, zu dem eine Verbindung hergestellt werden soll

[retval]: Gültiger Dateideskriptor im Erfolgsfall, andernfalls -1.

tipc_close()

Schließt die Verbindung zum Trusty-Dienst, der in einem Dateideskriptor angegeben ist.

int tipc_close(int fd);

[in] fd: Dateideskriptor zuvor geöffnet von ein tipc_connect()-Anruf

Kernel Trusty IPC-Client-API

Die Kernel Trusty IPC Client API ist für Kernel-Treiber verfügbar. Der Nutzer Space Die Trusty IPC API wird zusätzlich zu dieser API implementiert.

Im Allgemeinen besteht die typische Verwendung dieser API darin, dass ein Aufrufer ein struct tipc_chan-Objekt mithilfe der tipc_create_channel() und dann mithilfe des tipc_chan_connect()-Aufrufs eine Verbindung zum Trusty IPC-Dienst, der auf dem sicheren zu verstehen. Die Verbindung zur Remoteseite kann beendet werden, indem Sie tipc_chan_shutdown() wird angerufen, gefolgt von tipc_chan_destroy(), um Ressourcen zu bereinigen.

Beim Empfang einer Benachrichtigung (über den handle_event()-Callback) dass eine Verbindung hergestellt wurde, tut der Aufrufer Folgendes:

  • Ruft einen Nachrichtenpuffer mithilfe des tipc_chan_get_txbuf_timeout()-Aufrufs ab.
  • eine Nachricht verfassen und
  • Stellt die Nachricht mithilfe von tipc_chan_queue_msg() in die Warteschlange Methode zur Übermittlung an einen Trusty-Dienst (auf der sicheren Seite), an die der Kanal ist verbunden

Nach erfolgreicher Warteschlange sollte der Aufrufer den Nachrichtenpuffer vergessen. da der Nachrichtenpuffer schließlich in den kostenlosen Pufferpool zurückkehrt, Verarbeitung durch die Remote-Seite (zur späteren Wiederverwendung für andere Nachrichten) Der Nutzer muss nur tipc_chan_put_txbuf() aufrufen, wenn dies nicht funktioniert. dass er nicht mehr benötigt wird.

Ein API-Nutzer empfängt Nachrichten von der Remote-Seite, indem er eine handle_msg()-Benachrichtigungs-Callback, der in Kontext der rx-Arbeitswarteschlange „trusty-ipc“), die stellt einen Verweis auf einen rx-Zwischenspeicher bereit, der ein die zu verarbeiten ist.

Der handle_msg()-Callback wird erwartet, -Implementierung gibt einen Zeiger auf eine gültige struct tipc_msg_buf zurück. Sie kann mit dem Zwischenspeicher für eingehende Nachrichten identisch sein, wenn er lokal verarbeitet wird. und ist nicht mehr erforderlich. Alternativ kann es sich um einen neuen Puffer handeln, der durch einen tipc_chan_get_rxbuf()-Aufruf, wenn sich der eingehende Puffer in der Warteschlange befindet zur weiteren Verarbeitung an. Ein getrennter rx-Zwischenspeicher muss verfolgt werden und schließlich mit einem tipc_chan_put_rxbuf()-Aufruf veröffentlicht, wird es nicht mehr benötigt.

Methoden in der Kernel Trusty IPC Client API

tipc_create_channel()

Erstellt und konfiguriert eine Instanz eines Trusty-IPC-Kanals für eine bestimmte vertrauenswürdigen IPC-Gerät.

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

[in] dev: Zeiger auf die vertrauenswürdige IPC, für die das Gerät verwendet wird Kanal wurde erstellt

[in] ops: Zeiger auf ein struct tipc_chan_ops, mit anruferspezifischem Callbacks ausgefüllt

[in] cb_arg: Zeiger auf Daten, die übergeben werden an tipc_chan_ops Callbacks

[retval]: Zeiger auf eine neu erstellte Instanz von struct tipc_chan bei Erfolg, Andernfalls ERR_PTR(err)

Im Allgemeinen muss ein Aufrufer zwei Callbacks bereitstellen, die asynchron aufgerufen werden. wann die entsprechende Aktivität stattfindet.

Das Ereignis void (*handle_event)(void *cb_arg, int event) wird aufgerufen , um einen Anrufer über eine Änderung des Kanalstatus zu informieren.

[in] cb_arg: Zeiger auf Daten, die an einen übergebenen tipc_create_channel() Anruf

[in] event: Ein Ereignis, das einer der folgenden Werte sein kann:

  • TIPC_CHANNEL_CONNECTED: zeigt eine erfolgreiche Verbindung an. auf die Remote-Seite
  • TIPC_CHANNEL_DISCONNECTED: Gibt an, dass die Remote-Seite den neue Verbindungsanfrage stellen oder angefordert Trennung des zuvor verbundenen Kanals
  • TIPC_CHANNEL_SHUTDOWN: gibt an, dass die Remote-Seite heruntergefahren wird. dass alle Verbindungen dauerhaft beendet werden.

Das struct tipc_msg_buf *(*handle_msg)(void *cb_arg, struct tipc_msg_buf *mb) Callback wird aufgerufen, um eine Benachrichtigung darüber zu senden, dass eine neue Nachricht über einen bestimmten Kanal empfangen:

  • [in] cb_arg: Zeiger auf Daten, die an die tipc_create_channel() Anruf
  • [in] mb: Zeiger auf struct tipc_msg_buf eine eingehende Nachricht beschreiben
  • [retval]: Es wird erwartet, dass die Callback-Implementierung einen Zeiger auf einen struct tipc_msg_buf, die derselbe Zeiger sein können, als Parameter mb, wenn die Nachricht lokal verarbeitet wird und nicht nicht mehr erforderlich ist (oder kann ein neuer Zwischenspeicher sein, der durch den tipc_chan_get_rxbuf()-Aufruf abgerufen wird.)

tipc_chan_connect()

Initiiert eine Verbindung zum angegebenen Trusty IPC-Dienst.

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

[in] chan: Zeiger auf einen Kanal, der vom tipc_create_chan() Anruf

[in] port: Zeiger auf einen String mit dem Name des Dienstes, mit dem eine Verbindung hergestellt werden soll

[retval]: 0 bei Erfolg, andernfalls ein negativer Fehler

Der Anrufer wird benachrichtigt, wenn eine Verbindung hergestellt wird, indem er eine handle_event-Callback.

tipc_chan_shutdown()

Beendet eine zuvor initiierte Verbindung zum Trusty IPC-Dienst durch einen tipc_chan_connect()-Aufruf.

int tipc_chan_shutdown(struct tipc_chan *chan);

[in] chan: Zeiger auf einen Kanal, der von zurückgegeben wird ein tipc_create_chan()-Anruf

tipc_chan_datasets

Zerstört einen angegebenen Trusty-IPC-Kanal.

void tipc_chan_destroy(struct tipc_chan *chan);

[in] chan: Zeiger auf einen Kanal, der vom tipc_create_chan() Anruf

tipc_chan_get_txbuf_timeout()

Ruft einen Nachrichtenpuffer ab, der zum Senden von Daten über einen bestimmten Kanal. Wenn der Zwischenspeicher nicht sofort verfügbar ist, wird der Anrufer möglicherweise blockiert für das angegebene Zeitlimit (in Millisekunden).

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

[in] chan: Zeiger auf den Kanal, auf den eine Nachricht in die Warteschlange gestellt werden soll

[in] chan: Maximales Zeitlimit für die Wartezeit bis zum tx Zwischenspeicher wird verfügbar

[retval]: Ein gültiger Nachrichtenpuffer bei Erfolg, ERR_PTR(err) bei Fehler

tipc_chan_queue_msg()

Stellt eine Nachricht in die Warteschlange, die über die angegebene Vertrauenswürdige IPC-Kanäle.

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

[in] chan: Zeiger auf den Kanal, auf den die Nachricht in die Warteschlange gestellt werden soll

[in] mb: Zeiger auf die Nachricht, die in die Warteschlange gestellt werden soll (durch einen tipc_chan_get_txbuf_timeout()-Aufruf erhalten)

[retval]: 0 bei Erfolg, andernfalls ein negativer Fehler

tipc_chan_put_txbuf()

Gibt den angegebenen Nachrichtenpuffer vom Typ Tx frei zuvor durch einen tipc_chan_get_txbuf_timeout()-Aufruf abgerufen.

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

[in] chan: Zeiger auf den Kanal, zu dem gehört zu diesem Nachrichtenpuffer

[in] mb: Zeiger auf den Nachrichtenpuffer, der freigegeben werden soll

[retval]: Keine

tipc_chan_get_rxbuf()

Erhält einen neuen Nachrichtenpuffer, der zum Empfangen von Nachrichten über die angegeben ist.

struct tipc_msg_buf *tipc_chan_get_rxbuf(struct tipc_chan *chan);

[in] chan: Zeiger auf einen Kanal, zu dem dieser Nachrichtenpuffer gehört

[retval]: Ein gültiger Nachrichtenpuffer bei Erfolg, ERR_PTR(err) bei Fehler

tipc_chan_put_rxbuf()

Gibt einen angegebenen Nachrichtenpuffer frei, der zuvor von einem tipc_chan_get_rxbuf() Anruf.

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

[in] chan: Zeiger auf einen Kanal, zu dem dieser Nachrichtenpuffer gehört

[in] mb: Zeiger auf einen Nachrichtenzwischenspeicher, der freigegeben werden soll

[retval]: Keine