aos/lib/aos/aos_urpc.c
2022-05-18 14:35:00 +00:00

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