/*
* Copyright ( c ) 2016 , 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 "tipc_dev_ql.h"
#include <assert.h>
#include <compiler.h>
#include <err.h>
#include <inttypes.h>
#include <kernel/mutex.h>
#include <kernel/vm.h>
#include <lib/sm/sm_err.h>
#include <lib/trusty/handle.h>
#include <lib/trusty/handle_set.h>
#include <lib/trusty/ipc.h>
#include <lib/trusty/ipc_msg.h>
#include <list.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <trace.h>
#define LOCAL_TRACE 0
/*
* Max number of sync tipc devices
*/
#define QL_TIPC_DEV_MAX_NUM 2
/*
* Max number of opened channels supported
*/
#define QL_TIPC_ADDR_MAX_NUM 32
/*
* Local addresses base
*/
#define QL_TIPC_ADDR_BASE 32
/*
* Maximum service name size
*/
#define TIPC_MAX_SRV_NAME_LEN (256 )
struct tipc_ept {
struct handle* chan;
struct handle_ref* ref;
uint64_t cookie;
};
struct ql_tipc_dev {
struct list_node node;
struct handle* handle_set;
bool in_use; /* protected by @_dev_list_lock */
uint ns_mmu_flags;
ns_size_t ns_sz;
ext_mem_client_id_t client_id;
ext_mem_obj_id_t buf_id;
void * ns_va;
const uuid_t* uuid;
unsigned long inuse[BITMAP_NUM_WORDS(QL_TIPC_ADDR_MAX_NUM)];
struct tipc_ept epts[QL_TIPC_ADDR_MAX_NUM];
};
struct tipc_cmd_hdr {
uint16_t opcode;
uint16_t flags;
uint32_t status;
uint32_t handle;
uint32_t payload_len;
};
struct tipc_event {
uint32_t event;
uint32_t handle;
uint64_t cookie;
};
struct tipc_wait_req {
uint64_t reserved;
};
struct tipc_connect_req {
uint64_t cookie;
uint64_t reserved;
uint8_t name[0 ];
};
#ifdef SPIN_LOCK_FLAG_IRQ_FIQ
#define SLOCK_FLAGS SPIN_LOCK_FLAG_IRQ_FIQ
#else
#define SLOCK_FLAGS SPIN_LOCK_FLAG_INTERRUPTS
#endif
static uint _dev_cnt;
/*
* @ _ dev_list is only modified from stdcalls with _ dev_list_lock held . It can
* be read from any context with @ _ dev_list_lock held and from stdcalls without
* the @ _ dev_list_lock held .
*/
static struct list_node _dev_list = LIST_INITIAL_VALUE(_dev_list);
/* @_dev_list_lock protects @_dev_list and @struct ql_tipc_dev->in_use */
static spin_lock_t _dev_list_lock = SPIN_LOCK_INITIAL_VALUE;
static inline uint addr_to_slot(uint32_t addr) {
return (uint)(addr - QL_TIPC_ADDR_BASE);
}
static inline uint32_t slot_to_addr(uint slot) {
return (uint32_t)(slot + QL_TIPC_ADDR_BASE);
}
static uint32_t alloc_local_addr(struct ql_tipc_dev* dev,
struct handle* chan,
uint64_t cookie) {
int slot = bitmap_ffz(dev->inuse, QL_TIPC_ADDR_MAX_NUM);
if (slot >= 0 ) {
bitmap_set(dev->inuse, slot);
dev->epts[slot].chan = chan;
dev->epts[slot].cookie = cookie;
return slot_to_addr(slot);
}
return 0 ;
}
static struct tipc_ept* ept_lookup(struct ql_tipc_dev* dev, uint32_t local) {
uint slot = addr_to_slot(local);
if (slot < QL_TIPC_ADDR_MAX_NUM) {
if (bitmap_test(dev->inuse, slot)) {
return &dev->epts[slot];
}
}
return NULL;
}
static uint32_t ept_to_addr(struct ql_tipc_dev* dev, struct tipc_ept* ept) {
return slot_to_addr(ept - dev->epts);
}
static void free_local_addr(struct ql_tipc_dev* dev, uint32_t local) {
uint slot = addr_to_slot(local);
if (slot < QL_TIPC_ADDR_MAX_NUM) {
bitmap_clear(dev->inuse, slot);
dev->epts[slot].chan = NULL;
dev->epts[slot].cookie = 0 ;
}
}
static struct ql_tipc_dev* dev_lookup(ext_mem_client_id_t client_id,
ext_mem_obj_id_t buf_id) {
struct ql_tipc_dev* dev;
list_for_every_entry(&_dev_list, dev, struct ql_tipc_dev, node) {
if (dev->client_id == client_id && dev->buf_id == buf_id) {
return dev;
}
}
return NULL;
}
static struct ql_tipc_dev* dev_acquire(ext_mem_client_id_t client_id,
ext_mem_obj_id_t buf_id) {
struct ql_tipc_dev* dev;
spin_lock_saved_state_t state;
spin_lock_save(&_dev_list_lock, &state, SLOCK_FLAGS);
dev = dev_lookup(client_id, buf_id);
if (dev) {
if (dev->in_use) {
TRACEF("0x%" PRIx64 ": device in use by another cpu\n" , buf_id);
dev = NULL;
} else {
dev->in_use = true ;
}
}
spin_unlock_restore(&_dev_list_lock, state, SLOCK_FLAGS);
return dev;
}
static void dev_release(struct ql_tipc_dev* dev) {
spin_lock_saved_state_t state;
spin_lock_save(&_dev_list_lock, &state, SLOCK_FLAGS);
DEBUG_ASSERT(dev->in_use);
dev->in_use = false ;
spin_unlock_restore(&_dev_list_lock, state, SLOCK_FLAGS);
}
static long dev_create(ext_mem_client_id_t client_id,
ext_mem_obj_id_t buf_id,
ns_size_t buf_sz,
uint buf_mmu_flags) {
status_t res;
struct ql_tipc_dev* dev;
spin_lock_saved_state_t state;
dev = dev_lookup(client_id, buf_id);
if (dev) {
LTRACEF("0x%" PRIx64 ": device already exists\n" , buf_id);
return SM_ERR_INVALID_PARAMETERS;
}
if (!buf_sz) {
LTRACEF("zero size shared buffer specified\n" );
return SM_ERR_INVALID_PARAMETERS;
}
if (buf_sz & (PAGE_SIZE - 1 )) {
LTRACEF("shared buffer size is not page aligned: 0x%x\n" , buf_sz);
return SM_ERR_INVALID_PARAMETERS;
}
if (_dev_cnt >= QL_TIPC_DEV_MAX_NUM) {
LTRACEF("max number of devices reached: %d\n" , _dev_cnt);
return SM_ERR_NOT_ALLOWED;
}
dev = calloc(1 , sizeof (*dev));
if (!dev) {
LTRACEF("out of memory creating sync tipc dev\n" );
return SM_ERR_INTERNAL_FAILURE;
}
dev->uuid = &zero_uuid;
list_clear_node(&dev->node);
dev->handle_set = handle_set_create();
if (!dev->handle_set) {
LTRACEF("out of memory creating handle_set\n" );
free(dev);
return SM_ERR_INTERNAL_FAILURE;
}
/* map shared buffer into address space */
dev->client_id = client_id;
dev->buf_id = buf_id;
dev->ns_sz = buf_sz;
dev->ns_mmu_flags = buf_mmu_flags;
res = ext_mem_map_obj_id(vmm_get_kernel_aspace(), "tipc" , client_id, buf_id,
0 , 0 , round_up(buf_sz, PAGE_SIZE), &dev->ns_va,
PAGE_SIZE_SHIFT, 0 , buf_mmu_flags);
if (res != NO_ERROR) {
LTRACEF("failed (%d) to map shared buffer\n" , res);
free(dev);
return SM_ERR_INTERNAL_FAILURE;
}
spin_lock_save(&_dev_list_lock, &state, SLOCK_FLAGS);
list_add_head(&_dev_list, &dev->node);
spin_unlock_restore(&_dev_list_lock, state, SLOCK_FLAGS);
_dev_cnt++;
LTRACEF("tipc dev: %u bytes @ 0x%" PRIx64 ":0x%" PRIx64
" (%p) (flags=0x%x)\n" ,
dev->ns_sz, dev->client_id, dev->buf_id, dev->ns_va,
dev->ns_mmu_flags);
return 0 ;
}
static void dev_shutdown(struct ql_tipc_dev* dev) {
spin_lock_saved_state_t state;
DEBUG_ASSERT(dev);
DEBUG_ASSERT(dev->ns_va);
DEBUG_ASSERT(dev->in_use);
/* remove from list */
spin_lock_save(&_dev_list_lock, &state, SLOCK_FLAGS);
list_delete(&dev->node);
spin_unlock_restore(&_dev_list_lock, state, SLOCK_FLAGS);
_dev_cnt--;
/* unmap shared region */
vmm_free_region(vmm_get_kernel_aspace(), (vaddr_t)dev->ns_va);
dev->ns_va = NULL;
/* close all channels */
for (uint slot = 0 ; slot < countof(dev->epts); slot++) {
struct tipc_ept* ept = &dev->epts[slot];
if (!bitmap_test(dev->inuse, slot))
continue ;
if (!ept->chan)
continue ;
handle_set_detach_ref(ept->ref);
handle_decref(ept->chan);
free(ept->ref);
ept->ref = NULL;
handle_set_cookie(ept->chan, NULL);
handle_close(ept->chan);
}
free(dev);
}
static long set_status(struct ql_tipc_dev* dev, int cmd, int err, size_t len) {
struct tipc_cmd_hdr* ns_hdr = dev->ns_va;
ns_hdr->status = (err < 0 ) ? 1 : 0 ;
ns_hdr->payload_len = len;
ns_hdr->opcode = cmd | QL_TIPC_DEV_RESP;
smp_wmb();
return err;
}
static int dev_connect(struct ql_tipc_dev* dev,
void * ns_payload,
size_t ns_payload_len) {
int rc;
uint32_t local = 0 ;
struct handle* chan = NULL;
int opcode = QL_TIPC_DEV_CONNECT;
struct handle_ref* ref;
struct tipc_cmd_hdr* ns_hdr = dev->ns_va;
struct {
struct tipc_connect_req hdr;
uint8_t body[TIPC_MAX_SRV_NAME_LEN + 1 ];
} req;
if (ns_payload_len <= sizeof (req.hdr))
return set_status(dev, opcode, ERR_INVALID_ARGS, 0 );
if (ns_payload_len - sizeof (req.hdr) >= sizeof (req.body))
return set_status(dev, opcode, ERR_INVALID_ARGS, 0 );
/* copy out and zero terminate */
memcpy(&req, ns_payload, ns_payload_len);
req.body[ns_payload_len - sizeof (req.hdr)] = 0 ;
/* open ipc channel */
rc = ipc_port_connect_async(dev->uuid, (const char *)req.body,
ns_payload_len - sizeof (req.hdr), 0 , &chan);
if (rc != NO_ERROR) {
TRACEF("failed to open ipc channel: %d\n" , rc);
return set_status(dev, opcode, rc, 0 );
}
/* allocate slot */
local = alloc_local_addr(dev, chan, req.hdr.cookie);
if (local == 0 ) {
TRACEF("failed to alloc local address\n" );
handle_close(chan);
chan = NULL;
return set_status(dev, opcode, ERR_NO_RESOURCES, 0 );
}
LTRACEF("new handle: 0x%x\n" , local);
handle_set_cookie(chan, ept_lookup(dev, local));
ref = calloc(1 , sizeof (*ref));
if (!ref) {
rc = ERR_NO_MEMORY;
goto err_alloc_ref;
}
handle_incref(chan);
ref->handle = chan;
ref->emask = ~0 U;
ref->cookie = ept_lookup(dev, local);
ref->id = local;
rc = handle_set_attach(dev->handle_set, ref);
if (rc) {
goto err_handle_set_attach;
}
ept_lookup(dev, local)->ref = ref;
ns_hdr->handle = local;
return set_status(dev, opcode, 0 , 0 );
err_handle_set_attach:
handle_decref(chan);
free(ref);
err_alloc_ref:
free_local_addr(dev, local);
handle_close(chan);
chan = NULL;
return set_status(dev, opcode, rc, 0 );
}
static long dev_disconnect(struct ql_tipc_dev* dev, uint32_t target) {
struct tipc_ept* ept;
int opcode = QL_TIPC_DEV_DISCONNECT;
ept = ept_lookup(dev, target);
if (!ept || !ept->chan)
return SM_ERR_INVALID_PARAMETERS;
handle_set_detach_ref(ept->ref);
handle_decref(ept->chan);
free(ept->ref);
ept->ref = NULL;
handle_set_cookie(ept->chan, NULL);
handle_close(ept->chan);
free_local_addr(dev, target);
return set_status(dev, opcode, 0 , 0 );
}
static long dev_send(struct ql_tipc_dev* dev,
void * ns_data,
size_t ns_sz,
uint32_t target) {
int opcode = QL_TIPC_DEV_SEND;
struct tipc_ept* ept = ept_lookup(dev, target);
if (!ept || !ept->chan)
return set_status(dev, opcode, ERR_INVALID_ARGS, 0 );
struct ipc_msg_kern msg = {
.iov =
(struct iovec_kern[]){
[0 ] = {.iov_base = ns_data, .iov_len = ns_sz},
},
.num_iov = 1 ,
.num_handles = 0 };
return set_status(dev, opcode, ipc_send_msg(ept->chan, &msg), 0 );
}
static long dev_recv(struct ql_tipc_dev* dev, uint32_t target) {
int rc;
int opcode = QL_TIPC_DEV_RECV;
struct tipc_ept* ept = ept_lookup(dev, target);
if (!ept || !ept->chan)
return set_status(dev, opcode, ERR_INVALID_ARGS, 0 );
struct ipc_msg_info mi;
rc = ipc_get_msg(ept->chan, &mi);
if (rc < 0 )
return set_status(dev, opcode, rc, 0 );
struct ipc_msg_kern msg = {
.iov =
(struct iovec_kern[]){
[0 ] = {.iov_base = dev->ns_va +
sizeof (struct tipc_cmd_hdr),
.iov_len = dev->ns_sz -
sizeof (struct tipc_cmd_hdr)},
},
.num_iov = 1 ,
.num_handles = 0 };
rc = ipc_read_msg(ept->chan, mi.id, 0 , &msg);
ipc_put_msg(ept->chan, mi.id);
if (rc < 0 )
return set_status(dev, opcode, rc, 0 );
if (rc < (int )mi.len)
return set_status(dev, opcode, ERR_BAD_LEN, 0 );
return set_status(dev, opcode, rc, mi.len);
}
static long dev_has_event(struct ql_tipc_dev* dev,
void * ns_data,
size_t ns_sz,
uint32_t target) {
const int opcode = QL_TIPC_DEV_FC_HAS_EVENT;
/*
* Ignore ns_sz . The client sets payload_len to 0 since the payload is
* only used to return data , no data is passed in .
*
* Check that buffer is large enough for the response , even though this
* check can ' t fail with the current struct size , since dev - > ns_sz has
* already been checked to be page aligned and non - 0 .
*/
if ((sizeof (struct tipc_cmd_hdr) + sizeof (bool )) > dev->ns_sz) {
return set_status(dev, opcode, ERR_INVALID_ARGS, 0 );
}
bool * ready = (bool *)((uint8_t*)dev->ns_va + sizeof (struct tipc_cmd_hdr));
*ready = handle_set_ready(dev->handle_set);
return set_status(dev, opcode, 0 , sizeof (*ready));
}
static long dev_get_event(struct ql_tipc_dev* dev,
void * ns_data,
size_t ns_sz,
uint32_t target)
{
int rc;
struct handle* chan;
struct tipc_wait_req req;
uint32_t chan_event = 0 ;
struct tipc_ept* ept = NULL;
const int opcode = QL_TIPC_DEV_GET_EVENT;
struct tipc_event* evt = (struct tipc_event*)((uint8_t*)dev->ns_va +
sizeof (struct tipc_cmd_hdr));
if (ns_sz < sizeof (req) || ns_sz > dev->ns_sz)
return set_status(dev, opcode, ERR_INVALID_ARGS, 0 );
if (target) {
/* wait on specific handle */
ept = ept_lookup(dev, target);
if (!ept || !ept->chan)
return set_status(dev, opcode, ERR_INVALID_ARGS, 0 );
chan = ept->chan;
rc = handle_wait(chan, &chan_event, 0 );
if (rc == ERR_TIMED_OUT) {
/* no events return an empty event */
evt->handle = 0 ;
evt->event = 0 ;
evt->cookie = 0 ;
} else if (rc < 0 ) {
/* only possible if something is corrupted or somebody is
* already waiting on the same handle
*/
panic("%s: couldn't wait for handle events (%d)\n" , __func__, rc);
} else {
/* got an event: return it */
evt->handle = target;
evt->event = chan_event;
evt->cookie = ept->cookie;
}
} else {
struct handle_ref hsevt;
/* wait for event with 0-timeout */
rc = handle_set_wait(dev->handle_set, &hsevt, 0 );
if (rc == ERR_NOT_FOUND) {
/* no handles left */
return set_status(dev, opcode, rc, 0 );
}
if (rc < 0 ) {
if (rc == ERR_TIMED_OUT) {
/* no events: return an empty event */
evt->handle = 0 ;
evt->event = 0 ;
evt->cookie = 0 ;
} else {
/* only possible if somebody else is waiting
on the same handle which should never happen */
panic("%s: couldn't wait for handle events (%d)\n" , __func__,
rc);
}
} else {
/* got an event: return it */
ept = hsevt.cookie;
evt->handle = ept_to_addr(dev, ept);
evt->event = hsevt.emask;
evt->cookie = ept->cookie;
/* drop ref obtained by handle_set_wait */
handle_decref(hsevt.handle);
}
}
return set_status(dev, opcode, 0 , sizeof (*evt));
}
static long dev_handle_fc_cmd(struct ql_tipc_dev* dev,
const struct tipc_cmd_hdr* cmd,
void * ns_payload) {
DEBUG_ASSERT(dev);
switch (cmd->opcode) {
case QL_TIPC_DEV_FC_HAS_EVENT:
return dev_has_event(dev, ns_payload, cmd->payload_len, cmd->handle);
default :
LTRACEF("0x%x: unhandled cmd\n" , cmd->opcode);
return set_status(dev, cmd->opcode, ERR_NOT_SUPPORTED, 0 );
}
}
static long dev_handle_cmd(struct ql_tipc_dev* dev,
const struct tipc_cmd_hdr* cmd,
void * ns_payload) {
DEBUG_ASSERT(dev);
switch (cmd->opcode) {
case QL_TIPC_DEV_SEND:
return dev_send(dev, ns_payload, cmd->payload_len, cmd->handle);
case QL_TIPC_DEV_RECV:
return dev_recv(dev, cmd->handle);
case QL_TIPC_DEV_GET_EVENT:
return dev_get_event(dev, ns_payload, cmd->payload_len, cmd->handle);
case QL_TIPC_DEV_CONNECT:
return dev_connect(dev, ns_payload, cmd->payload_len);
case QL_TIPC_DEV_DISCONNECT:
return dev_disconnect(dev, cmd->handle);
default :
LTRACEF("0x%x: unhandled cmd\n" , cmd->opcode);
return set_status(dev, cmd->opcode, ERR_NOT_SUPPORTED, 0 );
}
}
long ql_tipc_create_device(ext_mem_client_id_t client_id,
ext_mem_obj_id_t buf_id,
ns_size_t buf_sz,
uint buf_mmu_flags) {
return dev_create(client_id, buf_id, buf_sz, buf_mmu_flags);
}
long ql_tipc_shutdown_device(ext_mem_client_id_t client_id,
ext_mem_obj_id_t buf_id) {
struct ql_tipc_dev* dev = dev_acquire(client_id, buf_id);
if (!dev) {
LTRACEF("0x%" PRIx64 ": device not found\n" , buf_id);
return SM_ERR_INVALID_PARAMETERS;
}
dev_shutdown(dev);
return 0 ;
}
long ql_tipc_handle_cmd(ext_mem_client_id_t client_id,
ext_mem_obj_id_t buf_id,
ns_size_t cmd_sz,
bool is_fc) {
long ret = SM_ERR_INVALID_PARAMETERS;
struct tipc_cmd_hdr cmd_hdr;
/* lookup device */
struct ql_tipc_dev* dev = dev_acquire(client_id, buf_id);
if (!dev) {
TRACEF("0x%" PRIx64 ": device not found\n" , buf_id);
goto err_not_found;
}
/* check for valid size */
if (cmd_sz < sizeof (cmd_hdr) || cmd_sz > dev->ns_sz) {
TRACEF("message size invalid (%zu)\n" , (size_t)cmd_sz);
goto err_invalid;
}
/* copy out command header */
memcpy(&cmd_hdr, dev->ns_va, sizeof (cmd_hdr));
/* check for consistency */
if (cmd_hdr.payload_len != (cmd_sz - sizeof (cmd_hdr))) {
TRACEF("malformed command\n" );
goto err_invalid;
}
if (is_fc) {
ret = dev_handle_fc_cmd(dev, &cmd_hdr, dev->ns_va + sizeof (cmd_hdr));
} else {
ret = dev_handle_cmd(dev, &cmd_hdr, dev->ns_va + sizeof (cmd_hdr));
}
err_invalid:
dev_release(dev);
err_not_found:
return ret;
}
Messung V0.5 in Prozent C=94 H=96 G=94
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet am 2026-06-27)
¤
*© Formatika GbR, Deutschland