Skip to content

WIP RPC that uses IPC #432

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ endif

SYSLIBS:=-lgcc -lc -lnosys
DEBUG:=-g3
WARNING:=-Wall -Werror
WARNING:=-Wall -Wstrict-overflow=3 -Werror

# Select optimizations depending on the build mode.
ifeq ("$(BUILD_MODE)","debug")
Expand Down
8 changes: 4 additions & 4 deletions api/inc/ipc_exports.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,15 @@ typedef enum uvisor_ipc_io_state {
UVISOR_IPC_IO_STATE_VALID, /* uVisor has copied the message */
} uvisor_ipc_io_state_t;

/* IPC Descriptor Structure */
/* When sending:
/* IPC Descriptor Structure
* When sending:
* @param[in] box_id the ID of the destination box
* @param[in] port the port to send the message to
* @param[in] len the length of the message
* @param[out] token a token that can be used to wait at a later time for
* the send to complete
*/
/* When receiving before a message has been received:
*
* When receiving before a message has been received:
* @param[in] box_id an ID of a box that is allowed to send to this box, or
* UVISOR_BOX_ID_ANY to allow messages from any box
* @param[in] port the port to listen for messages on
Expand Down
131 changes: 131 additions & 0 deletions api/inc/rpc2.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright (c) 2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef __UVISOR_API_RPC2_H__
#define __UVISOR_API_RPC2_H__

#include "api/inc/uvisor_exports.h"
#include <stdint.h>
#include <stddef.h>

typedef struct {
uint32_t magic;
} rpc2_gateway_t;

typedef int (* rpc2_fnptr_t)(uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3,
int box_id_caller);

/** RPC Call Descriptor
*
* This is the descriptor sent by RPC senders and handled by RPC receivers.
*
* @param[in] p0 first parameter
* @param[in] p1 second parameter
* @param[in] p2 third parameter
* @param[in] p3 fourth parameter
* @param[in] fnptr the function to call
* @param[in] completion_port the port number to wait for completion on
*/
typedef struct uvisor_rpc2_call {
uint32_t p0;
uint32_t p1;
uint32_t p2;
uint32_t p3;
rpc2_fnptr_t fnptr;
size_t completion_port;
} uvisor_rpc2_call_t;

/** RPC Return Descriptor
*
* This is the descriptor sent by RPC receivers and handled by RPC senders.
*
* @param[in] ret the return value from the call
*/
typedef struct uvisor_rpc2_return {
uint32_t ret;
} uvisor_rpc2_return_t;

/** RPC Completion Cookie
*
* This is used to wait for an RPC to complete.
*
* @param[in] completion_port the port number to wait for completion on
* @param[in] box_id the id of the box we expect completion from
*/
typedef struct uvisor_rpc2_cookie {
size_t completion_port;
int box_id;
} uvisor_rpc2_cookie_t;

/** Wait for incoming RPC.
*
* @param fn_ptr_array an array of RPC function targets that this call to
* `rpc_fncall_waitfor` should handle RPC to
* @param fn_count the number of function targets in this array
* @param box_id an ID of a box that is allowed to send to this box, or
* UVISOR_BOX_ID_ANY to allow messages from any box
* @param timeout_ms specifies how long to wait (in ms) for an incoming
* RPC message before returning
*/
UVISOR_EXTERN int rpc_waitfor(const rpc2_fnptr_t fn_array[], size_t fn_count, int box_id, uint32_t timeout_ms);

/** Start an asynchronous RPC.
*
* After this call successfully completes, the caller can, at any time in any
* thread, wait on the cookie to get the result of the call.
* @param[in] p0 first parameter
* @param[in] p1 second parameter
* @param[in] p2 third parameter
* @param[in] p3 fourth parameter
* @param[in] fnptr the function to call
* @param[in] box_id the ID of the box to call the function within
* @param[out] cookie wait on this cookie to get the result of the call
* @returns non-zero on error, zero on success
*/
UVISOR_EXTERN int rpc_async(uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3,
const rpc2_fnptr_t fnptr, int box_id, uvisor_rpc2_cookie_t * cookie);

/** Wait for an outgoing RPC to finish.
*
* Wait for the result of a previously started asynchronous RPC. After this
* call, ret will contain the return value of the RPC. The return value of this
* function may indicate that there was an error or a timeout with non-zero.
*
* @param cookie[in] The cookie to wait on for the result of an asynchronous RPC
* @param timeout_ms[in] How long to wait (in ms) for the asynchronous RPC
* message to finish before returning
* @param ret[out] The return value resulting from the finished RPC to
* the target function. Use NULL when don't care.
* @returns Non-zero on error or timeout, zero on successful wait
*/
UVISOR_EXTERN int rpc_wait(uvisor_rpc2_cookie_t cookie, uint32_t timeout_ms, uint32_t * ret);

/** Start an asynchronous RPC with a gateway.
*
* After this call successfully completes, the caller can, at any time in any
* thread, wait on the cookie to get the result of the call.
* @param[in] p0 first parameter
* @param[in] p1 second parameter
* @param[in] p2 third parameter
* @param[in] p3 fourth parameter
* @param[in] gateway the address of the RPC gateway
* @param[out] cookie wait on this cookie to get the result of the call
* @returns non-zero on error, zero on success
*/
UVISOR_EXTERN int rpc_gateway_async(uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3,
const rpc2_gateway_t * gateway, uvisor_rpc2_cookie_t * cookie);

#endif
1 change: 1 addition & 0 deletions api/inc/uvisor-lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "api/inc/interrupts.h"
#include "api/inc/register_gateway.h"
#include "api/inc/rpc.h"
#include "api/inc/rpc2.h"
#include "api/inc/ipc.h"
#include "api/inc/rpc_gateway.h"
#include "api/inc/secure_access.h"
Expand Down
223 changes: 223 additions & 0 deletions api/src/rpc2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
/*
* Copyright (c) 2017, ARM Limited, All Rights Reserved
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "api/inc/rpc2.h"
#include "api/inc/ipc.h"

/* A generic port for all incoming RPC. */
static const size_t UVISOR_RPC_PORT = 'f'; /* 'f' is for function. That's good enough for me. */

static uint32_t wait_for_all(uint32_t wait_tokens)
{
uint32_t done_tokens = 0;
static const uint32_t timeout_ms = 0;
static const uint32_t delay_iterations = 100000;

/* Spin until all tokens complete. */
while (ipc_waitforall(wait_tokens, &done_tokens, timeout_ms))
{
/* Waste time a bit. */
/* FIXME: When we can run RTOS in other boxes, or have a uVisor
* scheduler yield or sleep function, remove this room heater loop. */
for (volatile int i = 0; i <= delay_iterations; i++);
}

return done_tokens;
}

static int do_rpc(int caller_box_id, uvisor_rpc2_call_t * rpc_msg)
{
int status;
uint32_t done_tokens;
uvisor_rpc2_return_t rpc_ret;
uvisor_ipc_desc_t desc;

/* Do the RPC. */
rpc_ret.ret = rpc_msg->fnptr(rpc_msg->p0, rpc_msg->p1, rpc_msg->p2, rpc_msg->p3,
caller_box_id);

/* Send the RPC response to the port the caller box expects. */
desc.box_id = caller_box_id;
desc.port = rpc_msg->completion_port;
desc.len = sizeof(rpc_ret);

status = ipc_send(&desc, &rpc_ret);
if (status) {
return status;
}

/* Wait for the send to complete, to keep the IPC descriptor and RPC
* response in memory long enough for uVisor to read them (before they go
* out of scope). */
done_tokens = wait_for_all(desc.token);
if (!(done_tokens & desc.token)) {
/* Token we wanted didn't complete */
return -1;
}

return 0;
}

static int handle_rpc(int caller_box_id, uvisor_rpc2_call_t * rpc_msg, const rpc2_fnptr_t fn_array[], size_t fn_count)
{
size_t i;
/* Check if the target function is in the fn_array */
for (i = 0; i < fn_count; ++i) {
if (fn_array[i] == rpc_msg->fnptr) {
return do_rpc(caller_box_id, rpc_msg);
}
}

/* The RPC target was not in the list of acceptable RPC targets. */
return -1;
}

int rpc_waitfor(const rpc2_fnptr_t fn_array[], size_t fn_count, int box_id, uint32_t timeout_ms)
{
int status;
uvisor_ipc_desc_t desc = {0};
uvisor_rpc2_call_t rpc_msg;

/* Receive the RPC response over the generic RPC port. */
desc.box_id = box_id;
desc.port = UVISOR_RPC_PORT;
desc.len = sizeof(rpc_msg);

status = ipc_recv(&desc, &rpc_msg);
if (status) {
return status;
}

/* Wait for the receive to complete. */
if (timeout_ms == 0) {
/* Try once. */
uint32_t done_tokens = 0;
status = ipc_waitforall(desc.token, &done_tokens, timeout_ms);
if (status) {
return status;
}

/* See if done_tokens matches */
if (done_tokens != desc.token) {
/* Not good. No match. Fail. */
return -1;
}
} else {
/* Spin forever */
wait_for_all(desc.token);
}

/* Handle the RPC */
handle_rpc(desc.box_id, &rpc_msg, fn_array, fn_count);

return 0;
}

int rpc_async(uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3,
const rpc2_fnptr_t fnptr, int box_id, uvisor_rpc2_cookie_t *cookie)
{
int status;
uint32_t done_tokens;
size_t completion_port;
uvisor_rpc2_call_t rpc_msg = {0};
uvisor_ipc_desc_t desc = {0};

/* Allocate a return port to receive a response on. */
completion_port = 0; /* XXX TODO */

/* Build the RPC message. */
rpc_msg.p0 = p0;
rpc_msg.p1 = p1;
rpc_msg.p2 = p2;
rpc_msg.p3 = p3;
rpc_msg.fnptr = fnptr;
rpc_msg.completion_port = completion_port;

/* Send the message to the generic RPC port. */
desc.box_id = box_id;
desc.port = UVISOR_RPC_PORT;
desc.len = sizeof(rpc_msg);

status = ipc_send(&desc, &rpc_msg);
if (status) {
return status;
}

/* Wait for the send to complete, to keep the IPC descriptor and RPC
* message in memory long enough for uVisor to read them (before they go
* out of scope). */
done_tokens = wait_for_all(desc.token);
if (!(done_tokens & desc.token)) {
/* Token we wanted didn't complete */
return -1;
}

/* The message has been sent. Update the cookie with the port we should wait on. */
cookie->completion_port = completion_port;
cookie->box_id = box_id;

return 0;
}

int rpc_wait(uvisor_rpc2_cookie_t cookie, uint32_t timeout_ms, uint32_t * ret)
{
int status;
uvisor_ipc_desc_t desc = {0};
uvisor_rpc2_return_t rpc_ret = {0};

/* Receive the RPC response over the negotiated port for the call. */
desc.box_id = cookie.box_id;
desc.port = cookie.completion_port;
desc.len = sizeof(rpc_ret);

status = ipc_recv(&desc, &rpc_ret);
if (status) {
return status;
}

/* Wait for the receive to complete. */
if (timeout_ms == 0) {
/* Try once. */
uint32_t done_tokens = 0;
status = ipc_waitforall(desc.token, &done_tokens, timeout_ms);
if (status) {
return status;
}

/* See if done_tokens matches */
if (done_tokens != desc.token) {
/* Not good. No match. Fail. */
return -1;
}
} else {
/* Spin forever */
wait_for_all(desc.token);
}

/* If ret is provided, update ret with the RPC return value */
if (ret) {
*ret = rpc_ret.ret;
}

return 0;
}

int rpc_gateway_async(uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3,
const rpc2_gateway_t * gateway, uvisor_rpc2_cookie_t * cookie)
{
/* TODO */
return 0;
}
Loading