diff --git a/Makefile b/Makefile index 72d9e4ff..5bb73a00 100644 --- a/Makefile +++ b/Makefile @@ -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") diff --git a/api/inc/ipc_exports.h b/api/inc/ipc_exports.h index dde78797..72de5f3f 100644 --- a/api/inc/ipc_exports.h +++ b/api/inc/ipc_exports.h @@ -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 diff --git a/api/inc/rpc2.h b/api/inc/rpc2.h new file mode 100644 index 00000000..bf303c8a --- /dev/null +++ b/api/inc/rpc2.h @@ -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 +#include + +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 diff --git a/api/inc/uvisor-lib.h b/api/inc/uvisor-lib.h index 4cfbd950..7648c961 100644 --- a/api/inc/uvisor-lib.h +++ b/api/inc/uvisor-lib.h @@ -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" diff --git a/api/src/rpc2.c b/api/src/rpc2.c new file mode 100644 index 00000000..f8700eec --- /dev/null +++ b/api/src/rpc2.c @@ -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; +} diff --git a/core/vmpu/src/mpu_armv8m/vmpu_armv8m_unpriv_access.c b/core/vmpu/src/mpu_armv8m/vmpu_armv8m_unpriv_access.c index a1f54414..f443c377 100644 --- a/core/vmpu/src/mpu_armv8m/vmpu_armv8m_unpriv_access.c +++ b/core/vmpu/src/mpu_armv8m/vmpu_armv8m_unpriv_access.c @@ -93,7 +93,7 @@ extern int vmpu_fault_recovery_mpu(uint32_t pc, uint32_t sp, uint32_t fault_addr uint32_t vmpu_unpriv_access(uint32_t addr, uint32_t size, uint32_t data) { - int tries = 0; + unsigned int tries = 0; while(1) { if ((vmpu_unpriv_test_range(addr, UVISOR_UNPRIV_ACCESS_SIZE(size)) & (TT_RESP_NSRW_Msk | TT_RESP_SRVALID_Msk)) == (TT_RESP_NSRW_Msk | TT_RESP_SRVALID_Msk)) { switch(size) {