/*
* Copyright ( c ) 2019 , Google , Inc . All rights reserved
*
* Permission is hereby granted , free of charge , to any person obtaining
* a copy of this software and associated documentation files
* ( the " Software " ) , to deal in the Software without restriction ,
* including without limitation the rights to use , copy , modify , merge ,
* publish , distribute , sublicense , and / or sell copies of the Software ,
* and to permit persons to whom the Software is furnished to do so ,
* subject to the following conditions :
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED " AS IS " , WITHOUT WARRANTY OF ANY KIND ,
* EXPRESS OR IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY , FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT .
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM , DAMAGES OR OTHER LIABILITY , WHETHER IN AN ACTION OF CONTRACT ,
* TORT OR OTHERWISE , ARISING FROM , OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE .
*/
#include <assert.h>
#include <err.h>
#include <kernel/mutex.h>
#include <kernel/usercopy.h>
#include <lib/binary_search_tree.h>
#include <lib/syscall.h>
#include <list.h>
#include <platform/interrupts.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <trace.h>
#define LOCAL_TRACE 0
#include <lib/trusty/event.h>
#include <lib/trusty/handle.h>
#include <lib/trusty/trusty_app.h>
#include <lib/trusty/uctx.h>
#include <lib/trusty/uio.h>
/**
* enum event_state - event states
* @ EVENT_STATE_UNSIGNALED :
* state is an initial state of any event object . An event object leaves
* @ EVENT_STATE_UNSIGNALED state when it becomes signaled . An event object
* might return to @ EVENT_STATE_UNSIGNALED state under certain conditions
* ( see below ) .
* @ EVENT_STATE_SIGNALED :
* state is entered by event source object when event_source_signal ( ) routine
* is invoked . The @ EVENT_STATE_SIGNALED state is entered by event client
* object from @ EVENT_STATE_UNSIGNALED state in when event_source_signal ( )
* routine if invoke on event source or from @ EVENT_STATE_NOTIFIED_SIGNALED
* state when client acknowledges that previously delivered event has been
* handled . Waiting on event client object in @ EVENT_STATE_SIGNALED state
* generates the % IPC_HANDLE_POLL_MSG event for its waiters .
* @ EVENT_STATE_NOTIFIED :
* is entered by an event client object from @ EVENT_STATE_SIGNALED state
* after % IPC_HANDLE_POLL_MSG event has been delivered to the client . In
* this state , the event client object stops generating % IPC_HANDLE_POLL_MSG
* event for its waiters . The client should handle received event and
* acknowledge that by invoking event_client_notify_handled ( ) call on client
* event object . Upon receiving such acknowledgment the client event object
* is transitioning back to @ EVENT_STATE_UNSIGNALED state .
* @ EVENT_STATE_NOTIFIED_SIGNALED :
* is entered by an event client object from @ EVENT_STATE_NOTIFIED state
* when another signal is received which is possible for events sources that
* support edge triggering semantic . Receiving acknowledgment for event
* client in this state transition event object into @ EVENT_STATE_SIGNALED
* state which generates new % IPC_HANDLE_POLL_MSG event for its waiters .
* @ EVENT_STATE_HANDLED :
* is entered by an event source object from @ EVENT_STATE_SIGNALED state
* when all registered clients has finished handling an event and
* acknowledged that by invoking event_client_notify_handled ( ) routine .
* Waiting on event source object when it is in @ EVENT_STATE_HANDLED state
* generates % IPC_HANDLE_POLL_MSG event for its waiters and transition
* object back to @ EVENT_STATE_UNSIGNALED state .
* @ EVENT_STATE_CLOSED :
* is entered when the last reference to event source object handle goes
* away . This state is applicable for both event source and event client
* objects . In this state , the % IPC_HANDLE_POLL_HUP event is triggered to
* handle waiters .
*/
enum event_state {
EVENT_STATE_UNSIGNALED = 0 ,
EVENT_STATE_SIGNALED,
EVENT_STATE_NOTIFIED,
EVENT_STATE_NOTIFIED_SIGNALED,
EVENT_STATE_HANDLED,
EVENT_STATE_CLOSED,
};
/**
* struct event_source - represents event source object
* @ name : event name
* @ ops : pointed to @ struct event_source_ops
* @ ops_arg : pointer passes as & priv parameters of all ops callbacks
* @ uuids : pointer to array of & struct uuid items that are allowed to open
* this event source object
* @ uuids_num : number of items in @ uuids array
* @ refobj : ref object
* @ handle_ref : self reference from @ handle
* @ handle : embedded @ struct handle
* @ tree_node : tracking @ struct bst node
* @ client_list : list of attached clients
* @ client_cnt : number of attached clients
* @ slock : spinlock protecting internal state
* @ ack_cnt : required ack count
* @ state : event source state
*
* Note : the event object internal state and state transitions are protected by
* two locks : the global mutex ( & es_lock ) and a spin lock ( @ slock ) private to
* event source object . The global mutex is held to protect operations related
* to global event object list ( insert , remove and lookup ) and ref object .
* In addition , it is held to synchronize invocation of & open and & close
* callbacks which is happening in context of creating and destroying event
* objects . All other state transitions are protected by the spin lock .
*/
struct event_source {
const char * name;
const struct event_source_ops* ops;
const void * ops_arg;
const uuid_t* uuids;
unsigned int uuids_num;
struct obj refobj;
/* handle_ref is a self reference when there are
* outstanding handles out there . It is removed
* when last handle ref goes away .
*/
struct obj_ref handle_ref;
struct handle handle;
struct bst_node tree_node;
struct list_node client_list;
unsigned int client_cnt;
spin_lock_t slock;
unsigned int ack_cnt;
volatile int state;
};
struct event_client {
struct handle handle;
struct list_node node;
struct event_source* es;
struct obj_ref es_ref;
volatile int state;
};
#define SLOCK_FLAGS SPIN_LOCK_FLAG_INTERRUPTS
static uint32_t event_source_poll(struct handle* handle,
uint32_t emask,
bool finalize);
static void event_source_destroy(struct handle* handle);
static uint32_t event_client_poll(struct handle* handle,
uint32_t emask,
bool finalize);
static void event_client_destroy(struct handle* handle);
static ssize_t event_client_user_readv(struct handle* h,
user_addr_t iov_uaddr,
uint32_t iov_cnt);
static ssize_t event_client_user_writev(struct handle* handle,
user_addr_t iov_uaddr,
uint32_t iov_cnt);
static mutex_t es_lock = MUTEX_INITIAL_VALUE(es_lock);
static struct bst_root es_tree_root = BST_ROOT_INITIAL_VALUE;
static struct handle_ops event_source_handle_ops = {
.poll = event_source_poll,
.destroy = event_source_destroy,
};
static struct handle_ops event_client_handle_ops = {
.poll = event_client_poll,
.destroy = event_client_destroy,
.user_readv = event_client_user_readv,
.user_writev = event_client_user_writev,
};
/******************************************************************************/
static struct event_source* handle_to_event_source(struct handle* h) {
ASSERT(h);
ASSERT(h->ops == &event_source_handle_ops);
return containerof(h, struct event_source, handle);
}
static int event_source_bst_compare(struct bst_node* a, struct bst_node* b) {
struct event_source* es_a = containerof(a, struct event_source, tree_node);
struct event_source* es_b = containerof(b, struct event_source, tree_node);
return strcmp(es_a->name, es_b->name);
}
static uint32_t event_source_poll(struct handle* h,
uint32_t emask,
bool finalize) {
int oldstate;
spin_lock_saved_state_t state;
struct event_source* es = handle_to_event_source(h);
spin_lock_save(&es->slock, &state, SLOCK_FLAGS);
oldstate = es->state;
if (finalize && (oldstate == EVENT_STATE_HANDLED)) {
es->state = EVENT_STATE_UNSIGNALED;
}
spin_unlock_restore(&es->slock, state, SLOCK_FLAGS);
if (oldstate == EVENT_STATE_HANDLED) {
return IPC_HANDLE_POLL_MSG;
}
return 0 ;
}
static void event_source_obj_destroy(struct obj* obj) {
struct event_source* es = containerof(obj, struct event_source, refobj);
free(es);
}
static void event_source_destroy(struct handle* h) {
struct event_client* ec;
struct event_source* es;
spin_lock_saved_state_t state;
/* called when the last reference to handle goes away */
es = handle_to_event_source(h);
mutex_acquire(&es_lock);
/* if event source in global list : remove it */
if (es->tree_node.rank) {
bst_delete(&es_tree_root, &es->tree_node);
/* notify observers that event source is closed */
if (es->ops)
es->ops->close(es->ops_arg);
}
/* mark all clients still connected as closed */
spin_lock_save(&es->slock, &state, SLOCK_FLAGS);
es->state = EVENT_STATE_CLOSED;
list_for_every_entry(&es->client_list, ec, struct event_client, node) {
ec->state = EVENT_STATE_CLOSED;
handle_notify(&ec->handle);
}
spin_unlock_restore(&es->slock, state, SLOCK_FLAGS);
/* clear pointers that should not be accessed past this point */
es->ops = NULL;
es->ops_arg = NULL;
es->uuids = NULL;
es->name = NULL;
/* remove self reference */
obj_del_ref(&es->refobj, &es->handle_ref, event_source_obj_destroy);
mutex_release(&es_lock);
}
static struct event_source* event_source_lookup_locked(const char * name,
const uuid_t* uuid,
struct obj_ref* ref) {
struct bst_node* tn;
struct event_source* es;
struct event_source unused;
/* only init .name */
unused.name = name;
DEBUG_ASSERT(is_mutex_held(&es_lock));
tn = bst_search(&es_tree_root, &unused.tree_node, event_source_bst_compare);
if (!tn) {
/* Object not found */
return NULL;
}
/* Object found: check if we are allowed to connect */
es = containerof(tn, struct event_source, tree_node);
if (!es->uuids_num) {
/* No uuids are configured: allow anybody */
obj_add_ref(&es->refobj, ref);
return es;
}
/* check client */
for (uint32_t i = 0 ; i < es->uuids_num; i++) {
if (memcmp(uuid, &es->uuids[i], sizeof (*uuid)) == 0 ) {
obj_add_ref(&es->refobj, ref);
return es;
}
}
return NULL;
}
static void event_source_attach_client_locked(struct event_source* es,
struct event_client* ec) {
spin_lock_saved_state_t state;
DEBUG_ASSERT(is_mutex_held(&es_lock));
DEBUG_ASSERT(!spin_lock_held(&es->slock));
spin_lock_save(&es->slock, &state, SLOCK_FLAGS);
/* add ref to es and attach client to tracking list */
ec->es = es;
obj_add_ref(&es->refobj, &ec->es_ref);
list_add_tail(&es->client_list, &ec->node);
/* client starts in EVENT_STATE_UNSIGNALED state */
ec->state = EVENT_STATE_UNSIGNALED;
es->client_cnt++;
spin_unlock_restore(&es->slock, state, SLOCK_FLAGS);
if (es->client_cnt == 1 ) {
/* if first client (invokes open) */
if (es->ops && es->ops->open) {
es->ops->open(es->ops_arg);
}
}
}
static void event_source_notify_done_slocked(struct event_source* es) {
DEBUG_ASSERT(spin_lock_held(&es->slock));
ASSERT(es->ack_cnt > 0 );
/* decrement ack count of event source */
if (--es->ack_cnt == 0 ) {
/* All clients notified */
es->state = EVENT_STATE_HANDLED;
handle_notify(&es->handle);
if (es->ops && es->ops->unmask) {
es->ops->unmask(es->ops_arg);
}
}
}
int event_source_signal(struct handle* h) {
struct event_client* ec;
struct event_source* es;
spin_lock_saved_state_t state;
es = handle_to_event_source(h);
spin_lock_save(&es->slock, &state, SLOCK_FLAGS);
if (es->ops && es->ops->mask) {
/*
* If we have mask method we are in " level triggered " mode . It is
* expected that event should be signaled only if the event source is
* in EVENT_STATE_UNSIGNALED or EVENT_STATE_HANDLED state .
*/
ASSERT(es->state == EVENT_STATE_UNSIGNALED ||
es->state == EVENT_STATE_HANDLED);
/* mask source */
es->ops->mask(es->ops_arg);
}
if (es->client_cnt) {
/* we have clients */
es->ack_cnt = es->client_cnt;
es->state = EVENT_STATE_SIGNALED;
list_for_every_entry(&es->client_list, ec, struct event_client, node) {
if (ec->state == EVENT_STATE_UNSIGNALED) {
/* enter signaled state and pet handle */
ec->state = EVENT_STATE_SIGNALED;
handle_notify(&ec->handle);
} else if (ec->state == EVENT_STATE_NOTIFIED) {
/* enter signaled notify state */
ec->state = EVENT_STATE_NOTIFIED_SIGNALED;
}
}
} else {
/* no clients: mark source as handled and notify source handle */
es->state = EVENT_STATE_HANDLED;
handle_notify(&es->handle);
}
spin_unlock_restore(&es->slock, state, SLOCK_FLAGS);
return NO_ERROR;
}
int event_source_publish(struct handle* h) {
bool inserted;
struct event_source* es = handle_to_event_source(h);
mutex_acquire(&es_lock);
inserted =
bst_insert(&es_tree_root, &es->tree_node, event_source_bst_compare);
mutex_release(&es_lock);
return inserted ? NO_ERROR : ERR_ALREADY_EXISTS;
}
int event_source_create(const char * name,
const struct event_source_ops* ops,
const void * ops_arg,
const struct uuid* uuids,
unsigned int uuids_num,
unsigned int flags,
struct handle** ph) {
struct event_source* es;
if (!name || *name == 0 )
return ERR_INVALID_ARGS;
es = calloc(1 , sizeof (*es));
if (!es) {
return ERR_NO_MEMORY;
}
es->name = name;
if (ops) {
ASSERT(ops->open);
ASSERT(ops->close);
/* mask and unmask must be set together */
ASSERT(!ops->mask == !ops->unmask);
}
es->ops = ops;
es->ops_arg = ops_arg;
es->uuids = uuids;
es->uuids_num = uuids_num;
spin_lock_init(&es->slock);
list_initialize(&es->client_list);
bst_node_initialize(&es->tree_node);
obj_init(&es->refobj, &es->handle_ref);
handle_init(&es->handle, &event_source_handle_ops);
*ph = &es->handle;
return NO_ERROR;
}
int event_source_open(const uuid_t* cid,
const char * name,
size_t max_name,
uint flags,
struct handle** ph) {
int ret;
struct event_source* es;
struct event_client* ec = NULL;
struct obj_ref es_tmp_ref = OBJ_REF_INITIAL_VALUE(es_tmp_ref);
if (!name) {
return ERR_INVALID_ARGS;
}
size_t len = strnlen(name, max_name);
if (len == 0 || len >= max_name) {
/* empty or unterminated string */
LTRACEF("invalid path specified\n" );
return ERR_INVALID_ARGS;
}
/* After this point name is zero terminated */
mutex_acquire(&es_lock);
/* lookup event source */
es = event_source_lookup_locked(name, cid, &es_tmp_ref);
if (!es) {
ret = ERR_NOT_FOUND;
goto err_not_found;
}
/* allocate handle and tracking structure */
ec = calloc(1 , sizeof (*ec));
if (!ec) {
ret = ERR_NO_MEMORY;
goto err_alloc;
}
obj_ref_init(&ec->es_ref);
handle_init(&ec->handle, &event_client_handle_ops);
/* attach it to event source */
event_source_attach_client_locked(es, ec);
/* Looks OK */
handle_incref(&ec->handle);
*ph = &ec->handle;
ret = NO_ERROR;
err_attach:
err_alloc:
obj_del_ref(&es->refobj, &es_tmp_ref, event_source_obj_destroy);
err_not_found:
mutex_release(&es_lock);
if (ec) {
handle_decref(&ec->handle);
}
return ret;
}
/******************************************************************************/
static bool handle_is_client(struct handle* handle) {
ASSERT(handle);
return likely(handle->ops == &event_client_handle_ops);
}
static uint32_t event_client_poll(struct handle* h,
uint32_t emask,
bool finalize) {
int oldstate;
spin_lock_saved_state_t state;
ASSERT(handle_is_client(h));
struct event_client* ec = containerof(h, struct event_client, handle);
spin_lock_save(&ec->es->slock, &state, SLOCK_FLAGS);
oldstate = ec->state;
if (finalize && (oldstate == EVENT_STATE_SIGNALED)) {
ec->state = EVENT_STATE_NOTIFIED;
}
spin_unlock_restore(&ec->es->slock, state, SLOCK_FLAGS);
if (oldstate == EVENT_STATE_CLOSED) {
return IPC_HANDLE_POLL_HUP;
}
if (oldstate == EVENT_STATE_SIGNALED) {
return IPC_HANDLE_POLL_MSG;
}
return 0 ;
}
static void event_client_notify_done_slocked(struct event_client* ec) {
struct event_source* es = ec->es;
/* event source spinlock must be held. Global es_lock is not required */
DEBUG_ASSERT(spin_lock_held(&es->slock));
if (ec->state == EVENT_STATE_NOTIFIED_SIGNALED) {
/* back to signaled state and pet handle */
ec->state = EVENT_STATE_SIGNALED;
handle_notify(&ec->handle);
} else if (ec->state == EVENT_STATE_NOTIFIED) {
/* back to unsignaled state and update source */
ec->state = EVENT_STATE_UNSIGNALED;
event_source_notify_done_slocked(es);
}
}
static void event_client_destroy(struct handle* h) {
int oldstate;
struct event_client* ec;
struct event_source* es;
spin_lock_saved_state_t state;
ASSERT(handle_is_client(h));
ec = containerof(h, struct event_client, handle);
mutex_acquire(&es_lock);
es = ec->es;
ASSERT(es);
/* detach client */
spin_lock_save(&es->slock, &state, SLOCK_FLAGS);
oldstate = ec->state;
if (oldstate != EVENT_STATE_CLOSED) {
/* if source is not closed */
if (oldstate == EVENT_STATE_SIGNALED ||
oldstate == EVENT_STATE_NOTIFIED ||
oldstate == EVENT_STATE_NOTIFIED_SIGNALED) {
/* then invoke notify done */
event_source_notify_done_slocked(es);
}
ec->state = EVENT_STATE_CLOSED;
}
ASSERT(list_in_list(&ec->node));
list_delete(&ec->node);
es->client_cnt--;
spin_unlock_restore(&es->slock, state, SLOCK_FLAGS);
if (oldstate != EVENT_STATE_CLOSED) {
if (es->client_cnt == 0 ) {
/* last client: invoke close */
if (es->ops && es->ops->close) {
es->ops->close(es->ops_arg);
}
}
}
/* Remove reference to source object */
obj_del_ref(&es->refobj, &ec->es_ref, event_source_obj_destroy);
/* free client */
free(ec);
mutex_release(&es_lock);
}
int event_client_notify_handled(struct handle* h) {
int ret = NO_ERROR;
struct event_client* ec;
struct event_source* es;
spin_lock_saved_state_t state;
if (!handle_is_client(h)) {
return ERR_INVALID_ARGS;
}
ec = containerof(h, struct event_client, handle);
es = ec->es;
ASSERT(es);
spin_lock_save(&es->slock, &state, SLOCK_FLAGS);
switch (ec->state) {
case EVENT_STATE_NOTIFIED:
case EVENT_STATE_NOTIFIED_SIGNALED:
event_client_notify_done_slocked(ec);
break ;
case EVENT_STATE_CLOSED:
ret = ERR_CHANNEL_CLOSED;
break ;
default :
ret = ERR_BAD_STATE;
}
spin_unlock_restore(&es->slock, state, SLOCK_FLAGS);
return ret;
}
static ssize_t event_client_user_writev(struct handle* h,
user_addr_t iov_uaddr,
uint32_t iov_cnt) {
int ret;
ssize_t len;
uint32_t cmd;
DEBUG_ASSERT(h);
if (iov_cnt != 1 ) {
/* we expect exactly one iov here */
return ERR_INVALID_ARGS;
}
len = user_iovec_to_membuf((uint8_t*)&cmd, sizeof (cmd), iov_uaddr, iov_cnt);
if (len < 0 ) {
/* most likely FAULT */
return (int32_t)len;
}
if (len != sizeof (cmd)) {
/* partial write */
return ERR_INVALID_ARGS;
}
switch (cmd) {
case EVENT_NOTIFY_CMD_HANDLED:
ret = event_client_notify_handled(h);
break ;
default :
ret = ERR_INVALID_ARGS;
}
return ret;
}
static ssize_t event_client_user_readv(struct handle* h,
user_addr_t iov_uaddr,
uint32_t iov_cnt) {
return 0 ;
}
Messung V0.5 in Prozent C=94 H=96 G=94
¤ Dauer der Verarbeitung: 0.15 Sekunden
(vorverarbeitet am 2026-06-27)
¤
*© Formatika GbR, Deutschland