From 0a5106f42255112a8807e2b56cfc6f8ea900d5ac Mon Sep 17 00:00:00 2001 From: Jaeden Amero Date: Mon, 15 May 2017 15:54:04 +0100 Subject: [PATCH 1/4] ipc: Fix comment style --- api/inc/ipc_exports.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 From aabaa47576f774e1ef05b4cba393b3352178e8dd Mon Sep 17 00:00:00 2001 From: Jaeden Amero Date: Mon, 15 May 2017 16:45:56 +0100 Subject: [PATCH 2/4] rpc2: Add RPC2 --- api/inc/rpc2.h | 131 +++++++++++++++++++++++++ api/inc/uvisor-lib.h | 1 + api/src/rpc2.c | 223 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 355 insertions(+) create mode 100644 api/inc/rpc2.h create mode 100644 api/src/rpc2.c 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; +} From c3b82c7b35e9d26257544295e8e5b9d8c6b4a53e Mon Sep 17 00:00:00 2001 From: Jaeden Amero Date: Thu, 15 Jun 2017 14:50:21 +0100 Subject: [PATCH 3/4] armv8m: Don't allow signed integer overflow To avoid undefined behavior, replace the signed "times" counter with an unsigned one. We don't need to track negative times anyway. ./core/vmpu/src/mpu_armv8m/vmpu_armv8m_unpriv_access.c: In function 'vmpu_unpriv_access': ./core/vmpu/src/mpu_armv8m/vmpu_armv8m_unpriv_access.c:126:1: error: assuming signed overflow does not occur when changing X +- C1 cmp C2 to X cmp C2 -+ C1 [-Werror=strict-overflow] } ^ ./core/vmpu/src/mpu_armv8m/vmpu_armv8m_unpriv_access.c:94:10: error: assuming signed overflow does not occur when changing X +- C1 cmp C2 to X cmp C2 -+ C1 [-Werror=strict-overflow] uint32_t vmpu_unpriv_access(uint32_t addr, uint32_t size, uint32_t data) --- core/vmpu/src/mpu_armv8m/vmpu_armv8m_unpriv_access.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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) { From 5aba9d713762654a299dca9f025e5aacf24e4250 Mon Sep 17 00:00:00 2001 From: Jaeden Amero Date: Thu, 8 Jun 2017 11:21:52 +0100 Subject: [PATCH 4/4] Makefile: Warn on overflow undefined behavior To ensure our sanity checks stay sane, it's good to make sure we aren't invoking undefined overflow behavior. Make the compiler warn us when it attempts to take advantage of undefined overflow in optimizing. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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")