248 lines
6.8 KiB
C
248 lines
6.8 KiB
C
/**
|
|
* \file
|
|
* \brief inter core messaging
|
|
*/
|
|
|
|
/*
|
|
* Copyright (c) 2016, ETH Zurich.
|
|
* All rights reserved.
|
|
*
|
|
* This file is distributed under the terms in the attached LICENSE file.
|
|
* If you do not find this file, copies can be found by writing to:
|
|
* ETH Zurich D-INFK, Universitaetsstrasse 6, CH-8092 Zurich. Attn: Systems Group.
|
|
*/
|
|
|
|
#include <aos/aos_urpc.h>
|
|
#include <aos/kernel_cap_invocations.h>
|
|
#include <aos/waitset_chan.h>
|
|
#include <aos/aos_rpc.h>
|
|
#include <spawn/rpc_server.h>
|
|
#include <aos/deferred.h>
|
|
#include <aos/performance.h>
|
|
|
|
extern coreid_t my_core_id;
|
|
extern rpc_handler_t rpc_handlers[RPC_MTYPE_COUNT];
|
|
|
|
#ifdef PERFORMANCE_ENABLED
|
|
struct performance_context p;
|
|
#endif
|
|
|
|
errval_t do_aos_urpc(
|
|
struct aos_urpc *rpc, uintptr_t msg_type,
|
|
struct capref arg_cap, size_t arg_size, uintptr_t arg0, uintptr_t arg1,
|
|
struct capref *ret_cap, size_t *ret_size, uintptr_t *ret0, uintptr_t *ret1
|
|
) {
|
|
errval_t err;
|
|
|
|
// allocate the return capability before performing the rpc call
|
|
if(ret_cap != NULL) {
|
|
err = slot_alloc(ret_cap);
|
|
if (err_is_fail(err)) return err_push(err, LIB_ERR_SLOT_ALLOC);
|
|
}
|
|
|
|
assert(rpc->meta->call_in_progress == 0);
|
|
|
|
assert(capref_is_null(arg_cap));
|
|
rpc->meta->cap.type = ObjType_Null;
|
|
|
|
rpc->meta->a0 = msg_type;
|
|
rpc->meta->a1 = arg_size;
|
|
rpc->meta->a2 = arg0;
|
|
rpc->meta->a3 = arg1;
|
|
|
|
// memory barrier
|
|
__asm volatile (
|
|
"dmb sy\n"
|
|
);
|
|
|
|
rpc->meta->call_in_progress = 1;
|
|
|
|
// wait until the rpc call completes
|
|
while(rpc->meta->call_in_progress) {
|
|
// yield the thread because there might be useful stuff to do...
|
|
thread_yield();
|
|
}
|
|
|
|
// memory barrier
|
|
__asm volatile (
|
|
"dmb sy\n"
|
|
);
|
|
|
|
// read the response
|
|
if (ret_size != NULL) *ret_size = rpc->meta->a1;
|
|
if (ret0 != NULL) *ret0 = rpc->meta->a2;
|
|
if (ret1 != NULL) *ret1 = rpc->meta->a3;
|
|
|
|
if(ret_cap != NULL) {
|
|
if(rpc->meta->cap.type != ObjType_Null) {
|
|
if (rpc->meta->cap.type == ObjType_RAM) {
|
|
err = ram_forge(*ret_cap, rpc->meta->cap.u.ram.base, rpc->meta->cap.u.ram.bytes, my_core_id);
|
|
} else if (rpc->meta->cap.type == ObjType_Frame) {
|
|
err = frame_forge(*ret_cap, rpc->meta->cap.u.frame.base, rpc->meta->cap.u.frame.bytes, my_core_id);
|
|
} else if (rpc->meta->cap.type == ObjType_DevFrame) {
|
|
err = devframe_forge(*ret_cap, rpc->meta->cap.u.frame.base, rpc->meta->cap.u.frame.bytes, my_core_id);
|
|
} else {
|
|
err = LIB_ERR_URPC_BAD_CAPTYPE;
|
|
}
|
|
if (err_is_fail(err)) return err;
|
|
} else {
|
|
*ret_cap = NULL_CAP;
|
|
}
|
|
}
|
|
|
|
return rpc->meta->a0;
|
|
}
|
|
|
|
// URPC calls on the BSP core should only be done on this thread.
|
|
int urpc_client_loop(void *arg) {
|
|
errval_t err;
|
|
struct waitset *ws = arg;
|
|
while (true) {
|
|
err = event_dispatch(ws);
|
|
if (err_is_fail(err)) {
|
|
DEBUG_ERR(err, "in event_dispatch");
|
|
abort();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void urpc_server_send_reply(struct aos_urpc_server *urpc, errval_t err);
|
|
|
|
static void urpc_server_handler(void *arg) {
|
|
struct aos_urpc_server *urpc = arg;
|
|
errval_t err;
|
|
|
|
uintptr_t msg_type = urpc->meta->a0;
|
|
uintptr_t arg_size = urpc->meta->a1;
|
|
uintptr_t arg0 = urpc->meta->a2;
|
|
uintptr_t arg1 = urpc->meta->a3;
|
|
|
|
urpc->ret_cap = NULL_CAP;
|
|
urpc->meta->a1 = 0;
|
|
urpc->meta->a2 = 0;
|
|
urpc->meta->a3 = 0;
|
|
if (msg_type >= RPC_MTYPE_COUNT) {
|
|
err = AOS_ERR_RPC_UNKNOWN_MSG_TYPE;
|
|
} else if (arg_size > RPC_SHARED_SIZE) {
|
|
err = AOS_ERR_RPC_ARG_TOO_BIG;
|
|
} else {
|
|
rpc_handler_t handler = rpc_handlers[msg_type];
|
|
if (handler == NULL) {
|
|
err = AOS_ERR_RPC_UNKNOWN_MSG_TYPE;
|
|
} else {
|
|
err = handler(
|
|
&urpc->g,
|
|
NULL_CAP, arg_size, arg0, arg1,
|
|
&urpc->ret_cap, &urpc->meta->a1, &urpc->meta->a2, &urpc->meta->a3
|
|
);
|
|
}
|
|
}
|
|
|
|
if (err == AOS_ERR_RPC_ASYNC_REPLY) return;
|
|
urpc_server_send_reply(urpc, err);
|
|
}
|
|
|
|
static void urpc_server_send_reply(struct aos_urpc_server *urpc, errval_t err) {
|
|
urpc->meta->a0 = err;
|
|
if (!capref_is_null(urpc->ret_cap)) {
|
|
err = cap_direct_identify(urpc->ret_cap, &urpc->meta->cap);
|
|
if (err_is_fail(err)) {
|
|
DEBUG_ERR(err, "in cap_direct_identify while handling URPC");
|
|
abort();
|
|
}
|
|
// only allow ram capabilities or frame capabilities to be sent to other cores
|
|
assert(urpc->meta->cap.type == ObjType_RAM || urpc->meta->cap.type == ObjType_Frame);
|
|
}
|
|
|
|
// memory barrier
|
|
__asm volatile (
|
|
"dmb sy\n"
|
|
);
|
|
|
|
// tell the other side that we are done
|
|
urpc->meta->call_in_progress = false;
|
|
|
|
#ifdef PERFORMANCE_ENABLED
|
|
if(msg_type == RPC_MTYPE_NOP) {
|
|
perf_add_now(&p, "done");
|
|
}
|
|
#endif
|
|
|
|
// Unblock the URPC thread
|
|
thread_sem_post(&urpc->sem);
|
|
}
|
|
|
|
void urpc_server_async_reply(struct generic_rpc_server *g_rpc, errval_t ret_err) {
|
|
struct aos_urpc_server *urpc = (struct aos_urpc_server *)g_rpc;
|
|
urpc_server_send_reply(urpc, ret_err);
|
|
}
|
|
|
|
|
|
errval_t aos_urpc_get_bootinfo(struct aos_urpc * rpc, struct bootinfo_serialized ** ret)
|
|
{
|
|
size_t ret_len;
|
|
errval_t err;
|
|
err = do_aos_urpc(
|
|
rpc, RPC_MTYPE_GET_BOOTINFO,
|
|
NULL_CAP, 0, 0, 0,
|
|
NULL, &ret_len, NULL, NULL
|
|
);
|
|
if (err_is_fail(err)) {
|
|
return err;
|
|
}
|
|
|
|
*ret = malloc(ret_len);
|
|
if (*ret == NULL) return LIB_ERR_MALLOC_FAIL;
|
|
|
|
memcpy(*ret, rpc->shared_mem, ret_len);
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
int urpc_server(void *arg) {
|
|
struct aos_urpc_server *urpc = arg;
|
|
struct waitset *default_ws = get_default_waitset();
|
|
struct waitset_chanstate chan;
|
|
waitset_chanstate_init(&chan, CHANTYPE_OTHER);
|
|
|
|
thread_sem_init(&urpc->sem, 0);
|
|
|
|
while (true) {
|
|
// wait until an rpc call arrives
|
|
while(urpc->meta->call_in_progress == false) {
|
|
// yield the thread because there might be useful stuff to do...
|
|
thread_yield();
|
|
}
|
|
|
|
// memory barrier
|
|
__asm volatile (
|
|
"dmb sy\n"
|
|
);
|
|
|
|
#ifdef PERFORMANCE_ENABLED
|
|
bool type_nop = urpc->meta->a0 == RPC_MTYPE_NOP;
|
|
if(type_nop) {
|
|
perf_init(&p, "aos_urpc_server");
|
|
perf_add_now(&p, "start");
|
|
}
|
|
#endif
|
|
|
|
// handle the URPC on the same thread that handles regular RPCs, to avoid concurrency for the handlers
|
|
waitset_chan_trigger_closure(default_ws, &chan, MKCLOSURE(urpc_server_handler, urpc));
|
|
|
|
#ifdef PERFORMANCE_ENABLED
|
|
if(type_nop) {
|
|
perf_add_now(&p, "triggered_closure");
|
|
}
|
|
#endif
|
|
|
|
// wait until the rpc is handled
|
|
thread_sem_wait(&urpc->sem);
|
|
|
|
#ifdef PERFORMANCE_ENABLED
|
|
if(type_nop) {
|
|
perf_print(&p);
|
|
}
|
|
#endif
|
|
}
|
|
}
|