blob: 55a372c4eecb8cea71ffa4206b36c761bf0241e5 [file] [log] [blame]
/*
* Copyright 2019, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the BSD 2-Clause license. Note that NO WARRANTY is provided.
* See "LICENSE_BSD2.txt" for details.
*
* @TAG(DATA61_BSD)
*/
#include <autoconf.h>
#include <sel4nanopb/sel4nanopb.h>
#include <sel4rpc/server.h>
#include <sel4utils/api.h>
#include <simple/simple.h>
#include <vka/vka.h>
#include <vka/object.h>
#include <vka/capops.h>
#include <rpc.pb.h>
#include <pb_encode.h>
#include <pb_decode.h>
#include <utils/zf_log.h>
static int sel4rpc_handle_memory(sel4rpc_server_env_t *env, RpcMessage *rpcMsg)
{
cspacepath_t path;
int error;
if (rpcMsg->msg.memory.action == Action_ALLOCATE) {
error = vka_cspace_alloc_path(env->vka, &path);
if (error) {
ZF_LOGE("Failed to alloc path: %d\n", error);
sel4rpc_server_reply(env, 0, 1, 0);
return -1;
}
uintptr_t cookie;
error = vka_utspace_alloc_at(env->vka, &path, rpcMsg->msg.memory.type, rpcMsg->msg.memory.size_bits,
rpcMsg->msg.memory.address, &cookie);
if (error) {
ZF_LOGE("Failed to alloc at: %d\n", error);
vka_cspace_free_path(env->vka, path);
sel4rpc_server_reply(env, 0, 1, 0);
return -1;
}
seL4_SetCap(0, path.capPtr);
int ret = sel4rpc_server_reply(env, 1, 0, cookie);
vka_cnode_delete(&path);
vka_cspace_free_path(env->vka, path);
return ret;
} else {
vka_utspace_free(env->vka, rpcMsg->msg.memory.type, rpcMsg->msg.memory.size_bits,
rpcMsg->msg.memory.address);
return sel4rpc_server_reply(env, 0, 0, 0);
}
}
static int sel4rpc_handle_ioport(sel4rpc_server_env_t *env, RpcMessage *rpcMsg)
{
cspacepath_t path;
int error;
error = vka_cspace_alloc_path(env->vka, &path);
if (error) {
ZF_LOGE("Failed to alloc path: %d\n", error);
sel4rpc_server_reply(env, 0, 1, 0);
return -1;
}
seL4_Error err = simple_get_IOPort_cap(env->simple, rpcMsg->msg.ioport.start, rpcMsg->msg.ioport.end,
path.root, path.capPtr, path.capDepth);
if (err != seL4_NoError) {
vka_cspace_free_path(env->vka, path);
sel4rpc_server_reply(env, 0, 1, 0);
return err;
}
seL4_SetCap(0, path.capPtr);
int ret = sel4rpc_server_reply(env, 1, 0, 0);
vka_cnode_delete(&path);
vka_cspace_free_path(env->vka, path);
return ret;
}
static int sel4rpc_handle_irq(sel4rpc_server_env_t *env, RpcMessage *rpcMsg)
{
cspacepath_t path;
int error;
error = vka_cspace_alloc_path(env->vka, &path);
if (error) {
ZF_LOGE("Failed to alloc path: %d\n", error);
sel4rpc_server_reply(env, 0, 1, 0);
return -1;
}
seL4_Error err = seL4_InvalidArgument;
switch (rpcMsg->msg.irq.which_type) {
case IrqAllocMessage_msi_tag: {
/* x86 MSI IRQ */
#ifdef CONFIG_ARCH_X86
IrqAllocMessagex86_MSI *msi = &rpcMsg->msg.irq.type.msi;
err = arch_simple_get_msi(&env->simple->arch_simple, path, msi->pci_bus,
msi->pci_dev, msi->pci_func, msi->handle, msi->vector);
#endif
break;
}
case IrqAllocMessage_ioapic_tag: {
/* x86 IOAPIC IRQ */
#ifdef CONFIG_ARCH_X86
IrqAllocMessagex86_IOAPIC *ioapic = &rpcMsg->msg.irq.type.ioapic;
err = arch_simple_get_ioapic(&env->simple->arch_simple, path, ioapic->ioapic,
ioapic->pin, ioapic->level, ioapic->polarity, ioapic->vector);
#endif
break;
}
case IrqAllocMessage_simple_tag: {
/* Simple IRQ */
IrqAllocMessageSimple *simple = &rpcMsg->msg.irq.type.simple;
#ifdef CONFIG_ARCH_ARM
if (simple->setTrigger) {
err = arch_simple_get_IRQ_trigger(&env->simple->arch_simple, simple->irq,
simple->trigger, path);
} else
#endif
{
err = simple_get_IRQ_handler(env->simple, simple->irq, path);
}
break;
}
default:
ZF_LOGE("Unknown IRQ type");
vka_cspace_free_path(env->vka, path);
return -1;
}
if (err != seL4_NoError) {
vka_cspace_free_path(env->vka, path);
sel4rpc_server_reply(env, 0, 1, 0);
return err;
}
seL4_SetCap(0, path.capPtr);
int ret = sel4rpc_server_reply(env, 1, 0, 0);
vka_cnode_delete(&path);
vka_cspace_free_path(env->vka, path);
return ret;
}
int sel4rpc_default_handler(sel4rpc_server_env_t *env, UNUSED void *data, RpcMessage *rpcMsg)
{
cspacepath_t path;
path.capPtr = seL4_CapNull;
switch (rpcMsg->which_msg) {
case RpcMessage_memory_tag:
return sel4rpc_handle_memory(env, rpcMsg);
case RpcMessage_ioport_tag:
return sel4rpc_handle_ioport(env, rpcMsg);
case RpcMessage_irq_tag:
return sel4rpc_handle_irq(env, rpcMsg);
default:
ZF_LOGE("Not sure what to do!");
}
return -1;
}
int sel4rpc_server_init(sel4rpc_server_env_t *env, vka_t *vka,
sel4rpc_handler_t handler_func, void *data, vka_object_t *reply, simple_t *simple)
{
env->vka = vka;
env->reply = reply;
env->handler = handler_func;
env->data = data;
env->simple = simple;
return 0;
}
int sel4rpc_server_reply(sel4rpc_server_env_t *env, int caps, int errorCode, int cookie)
{
pb_ostream_t ostream = pb_ostream_from_IPC(0);
RpcMessage rpcMsg;
rpcMsg.which_msg = RpcMessage_ret_tag;
rpcMsg.msg.ret.errorCode = errorCode;
rpcMsg.msg.ret.cookie = cookie;
bool ret = pb_encode_delimited(&ostream, &RpcMessage_msg, &rpcMsg);
if (!ret) {
/* encode failed, clean up any caps */
while (caps > 0) {
caps--;
vka_cspace_free(env->vka, seL4_GetCap(caps));
}
ZF_LOGE("Failed to encode reply (%s)", PB_GET_ERROR(&ostream));
return -1;
}
size_t size = ostream.bytes_written / sizeof(seL4_Word);
if (ostream.bytes_written % sizeof(seL4_Word)) {
size++;
}
api_reply(env->reply->cptr, seL4_MessageInfo_new(0, 0, caps, size));
return 0;
}
int sel4rpc_server_recv(sel4rpc_server_env_t *env)
{
RpcMessage rpcMsg;
pb_istream_t stream = pb_istream_from_IPC(1);
bool ret = pb_decode_delimited(&stream, &RpcMessage_msg, &rpcMsg);
if (!ret) {
ZF_LOGE("Invalid protobuf stream (%s)", PB_GET_ERROR(&stream));
return -1;
}
int err = 0;
if (env->handler) {
err = env->handler(env, env->data, &rpcMsg);
} else {
err = sel4rpc_default_handler(env, NULL, &rpcMsg);
}
return err;
}