mbed-os

Fork of mbed-os by erkin yucel

Committer:
elessair
Date:
Sun Oct 23 15:10:02 2016 +0000
Revision:
0:f269e3021894
Initial commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
elessair 0:f269e3021894 1 # Quick-Start Guide for uVisor on mbed OS
elessair 0:f269e3021894 2
elessair 0:f269e3021894 3 This guide will help you get started with uVisor on mbed OS by walking you through creating a sample application for the NXP FRDM-K64F board.
elessair 0:f269e3021894 4
elessair 0:f269e3021894 5 The uVisor provides sandboxed environments and resources protection for applications built for ARM Cortex-M3 and Cortex-M4 devices. Here we will show you how to enable the uVisor and configure a secure box to get hold of some exclusive resources (memory, peripherals, interrupts). For more information on the uVisor design philosophy, please check out our the uVisor [introductory document](../../README.md).
elessair 0:f269e3021894 6
elessair 0:f269e3021894 7 ## Overview
elessair 0:f269e3021894 8
elessair 0:f269e3021894 9 To get a basic `blinky` application running on mbed OS with uVisor enabled, you will need the following:
elessair 0:f269e3021894 10
elessair 0:f269e3021894 11 * A platform and a toolchain supported by uVisor on mbed OS. You can verify this on [the official list](../../README.md#supported-platforms). Please note that uVisor might support some platform internally, but not on mbed OS. Generally this means that the porting process has only been partially completed. If you want to port your platform to uVisor and enable it on mbed OS, please follow the [uVisor Porting Guide for mbed OS](../core/PORTING.md).
elessair 0:f269e3021894 12 * git. It will be used to download the mbed codebase.
elessair 0:f269e3021894 13 * The mbed command-line tools, mbed-cli. You can run `pip install mbed-cli` to install them.
elessair 0:f269e3021894 14
elessair 0:f269e3021894 15 For the remainder of this guide we will assume the following:
elessair 0:f269e3021894 16
elessair 0:f269e3021894 17 * You are developing on a \*nix machine, in the `~/code` folder.
elessair 0:f269e3021894 18 * You are building the app for the [NXP FRDM-K64F](http://developer.mbed.org/platforms/FRDM-K64F/) target, with the [GCC ARM Embedded](https://launchpad.net/gcc-arm-embedded) toolchain.
elessair 0:f269e3021894 19
elessair 0:f269e3021894 20 The instructions provided can be easily generalized to the case of other targets on other host OSs.
elessair 0:f269e3021894 21
elessair 0:f269e3021894 22 ## Start with the `blinky` app
elessair 0:f269e3021894 23 [Go to top](#overview)
elessair 0:f269e3021894 24
elessair 0:f269e3021894 25 To create a new mbed application called `uvisor-example` just run the following commands:
elessair 0:f269e3021894 26
elessair 0:f269e3021894 27 ```bash
elessair 0:f269e3021894 28 $ cd ~/code
elessair 0:f269e3021894 29 $ mbed new uvisor-example
elessair 0:f269e3021894 30 ```
elessair 0:f269e3021894 31
elessair 0:f269e3021894 32 The mbed-cli tools will automatically fetch the mbed codebase for you. By default, git will be used to track your code changes, so your application will be ready to be pushed to a git server, if you want to.
elessair 0:f269e3021894 33
elessair 0:f269e3021894 34 Once the import process is finished, create a `source` folder:
elessair 0:f269e3021894 35 ```bash
elessair 0:f269e3021894 36 $ mkdir ~/code/uvisor-example/source
elessair 0:f269e3021894 37 ```
elessair 0:f269e3021894 38 and place a new file `main.cpp` in it:
elessair 0:f269e3021894 39
elessair 0:f269e3021894 40 ```C
elessair 0:f269e3021894 41 /* ~/code/uvisor-example/source/main.cpp */
elessair 0:f269e3021894 42
elessair 0:f269e3021894 43 #include "mbed.h"
elessair 0:f269e3021894 44 #include "rtos.h"
elessair 0:f269e3021894 45
elessair 0:f269e3021894 46 DigitalOut led(LED1);
elessair 0:f269e3021894 47
elessair 0:f269e3021894 48 int main(void)
elessair 0:f269e3021894 49 {
elessair 0:f269e3021894 50 while (true) {
elessair 0:f269e3021894 51 led = !led;
elessair 0:f269e3021894 52 Thread::wait(500);
elessair 0:f269e3021894 53 }
elessair 0:f269e3021894 54 }
elessair 0:f269e3021894 55 ```
elessair 0:f269e3021894 56
elessair 0:f269e3021894 57 This simple application just blinks an LED from the main thread, which is created by default by the OS.
elessair 0:f269e3021894 58
elessair 0:f269e3021894 59 ---
elessair 0:f269e3021894 60
elessair 0:f269e3021894 61 **Checkpoint**
elessair 0:f269e3021894 62
elessair 0:f269e3021894 63 Compile the application:
elessair 0:f269e3021894 64
elessair 0:f269e3021894 65 ```bash
elessair 0:f269e3021894 66 $ mbed compile -m K64F -t GCC_ARM
elessair 0:f269e3021894 67 ```
elessair 0:f269e3021894 68
elessair 0:f269e3021894 69 The resulting binary will be located at:
elessair 0:f269e3021894 70
elessair 0:f269e3021894 71 ```bash
elessair 0:f269e3021894 72 ~/code/uvisor-example/.build/K64F/GCC_ARM/uvisor-example.bin
elessair 0:f269e3021894 73 ```
elessair 0:f269e3021894 74
elessair 0:f269e3021894 75 Drag-and-drop it onto the USB device mounted on your computer in order to flash the device. When the flashing process is completed, press the reset button on the device. You should see the device LED blinking.
elessair 0:f269e3021894 76
elessair 0:f269e3021894 77 ---
elessair 0:f269e3021894 78
elessair 0:f269e3021894 79 In the next sections you will see:
elessair 0:f269e3021894 80
elessair 0:f269e3021894 81 * How to [enable uVisor](#enable-uvisor) on the `uvisor-example` app.
elessair 0:f269e3021894 82 * How to [add a secure box](#add-a-secure-box) to the `uvisor-example` app with exclusive access to a timer, to a push-button interrupt, and to static and dynamic memories.
elessair 0:f269e3021894 83
elessair 0:f269e3021894 84 ## Enable uVisor
elessair 0:f269e3021894 85 [Go to top](#overview)
elessair 0:f269e3021894 86
elessair 0:f269e3021894 87 To enable the uVisor on the app, just add the following lines at the beginning of the `main.cpp` file:
elessair 0:f269e3021894 88
elessair 0:f269e3021894 89 ```C
elessair 0:f269e3021894 90 /* ~/code/uvisor-example/source/main.cpp */
elessair 0:f269e3021894 91
elessair 0:f269e3021894 92 #include "mbed.h"
elessair 0:f269e3021894 93 #include "rtos.h"
elessair 0:f269e3021894 94 #include "uvisor-lib/uvisor-lib.h"
elessair 0:f269e3021894 95
elessair 0:f269e3021894 96 /* Register privleged system hooks.
elessair 0:f269e3021894 97 * This is a system-wide configuration and it is independent from the app, but
elessair 0:f269e3021894 98 * for the moment it needs to be specified in the app. This will change in a
elessair 0:f269e3021894 99 * later version: The configuration will be provided by the OS. */
elessair 0:f269e3021894 100 extern "C" void SVC_Handler(void);
elessair 0:f269e3021894 101 extern "C" void PendSV_Handler(void);
elessair 0:f269e3021894 102 extern "C" void SysTick_Handler(void);
elessair 0:f269e3021894 103 extern "C" uint32_t rt_suspend(void);
elessair 0:f269e3021894 104 UVISOR_SET_PRIV_SYS_HOOKS(SVC_Handler, PendSV_Handler, SysTick_Handler, rt_suspend);
elessair 0:f269e3021894 105
elessair 0:f269e3021894 106 /* Main box Access Control Lists (ACLs). */
elessair 0:f269e3021894 107 /* Note: These are specific to the NXP FRDM-K64F board. See the section below
elessair 0:f269e3021894 108 * for more information. */
elessair 0:f269e3021894 109 static const UvisorBoxAclItem g_main_box_acls[] = {
elessair 0:f269e3021894 110 /* For the LED */
elessair 0:f269e3021894 111 {SIM, sizeof(*SIM), UVISOR_TACLDEF_PERIPH},
elessair 0:f269e3021894 112 {PORTB, sizeof(*PORTB), UVISOR_TACLDEF_PERIPH},
elessair 0:f269e3021894 113
elessair 0:f269e3021894 114 /* For messages printed on the serial port. */
elessair 0:f269e3021894 115 {OSC, sizeof(*OSC), UVISOR_TACLDEF_PERIPH},
elessair 0:f269e3021894 116 {MCG, sizeof(*MCG), UVISOR_TACLDEF_PERIPH},
elessair 0:f269e3021894 117 {UART0, sizeof(*UART0), UVISOR_TACLDEF_PERIPH},
elessair 0:f269e3021894 118 };
elessair 0:f269e3021894 119
elessair 0:f269e3021894 120 /* Enable uVisor, using the ACLs we just created. */
elessair 0:f269e3021894 121 UVISOR_SET_MODE_ACL(UVISOR_ENABLED, g_main_box_acls);
elessair 0:f269e3021894 122
elessair 0:f269e3021894 123 /* Rest of the existing app code */
elessair 0:f269e3021894 124 ...
elessair 0:f269e3021894 125 ```
elessair 0:f269e3021894 126
elessair 0:f269e3021894 127 In the code above we specified 3 elements:
elessair 0:f269e3021894 128
elessair 0:f269e3021894 129 1. System-wide uVisor configurations: `UVISOR_SET_PRIV_SYS_HOOKS`. Application authors currently need to specify the privileged system hooks at the application level with this macro, but in the future the operating system will register the privileged system hooks on its own.
elessair 0:f269e3021894 130 1. Main box Access Control Lists (ACLs). Since with uVisor enabled everything runs in unprivileged mode, we need to make sure that peripherals that are accessed by the OS and the main box are allowed. These peripherals are specified using a list like the one in the snippet above. For the purpose of this example we provide you the list of all the ACLs that we know you will need. For other platforms or other applications you need to determine those ACLs following a process that is described in a [section](#the-main-box-acls) below.
elessair 0:f269e3021894 131 1. App-specific uVisor configurations: `UVISOR_SET_MODE_ACL`. This macro sets the uVisor mode (enabled) and associates the list of ACLs we just created with the main box.
elessair 0:f269e3021894 132
elessair 0:f269e3021894 133 Before compiling, we need to override the original `K64F` target to enable the uVisor feature. To do so, add the file `~/code/uvisor-example/mbed_app.json` with the following content:
elessair 0:f269e3021894 134
elessair 0:f269e3021894 135 ```JSON
elessair 0:f269e3021894 136 {
elessair 0:f269e3021894 137 "target_overrides": {
elessair 0:f269e3021894 138 "K64F": {
elessair 0:f269e3021894 139 "target.features_add": ["UVISOR"],
elessair 0:f269e3021894 140 "target.extra_labels_add": ["UVISOR_SUPPORTED"]
elessair 0:f269e3021894 141 }
elessair 0:f269e3021894 142 },
elessair 0:f269e3021894 143 "macros": [
elessair 0:f269e3021894 144 "FEATURE_UVISOR",
elessair 0:f269e3021894 145 "TARGET_UVISOR_SUPPORTED"
elessair 0:f269e3021894 146 ]
elessair 0:f269e3021894 147 }
elessair 0:f269e3021894 148 ```
elessair 0:f269e3021894 149
elessair 0:f269e3021894 150 The macros `FEATURE_UVISOR` and `TARGET_UVISOR_SUPPORTED` in the configuration file above are automatically defined for C and C++ files, but not for assembly files. Since the uVisor relies on those symbols in some assembly code, we need to define them manually.
elessair 0:f269e3021894 151
elessair 0:f269e3021894 152 ---
elessair 0:f269e3021894 153
elessair 0:f269e3021894 154 **Checkpoint**
elessair 0:f269e3021894 155
elessair 0:f269e3021894 156 Compile the application again. This time the `K64F` target will include the new features and labels we provided in `mbed_app.json`;
elessair 0:f269e3021894 157
elessair 0:f269e3021894 158 ```bash
elessair 0:f269e3021894 159 $ mbed compile -m K64F -t GCC_ARM
elessair 0:f269e3021894 160 ```
elessair 0:f269e3021894 161
elessair 0:f269e3021894 162 The binary will be located at:
elessair 0:f269e3021894 163
elessair 0:f269e3021894 164 ```bash
elessair 0:f269e3021894 165 ~/code/uvisor-example/.build/K64F/GCC_ARM/uvisor-example.bin
elessair 0:f269e3021894 166 ```
elessair 0:f269e3021894 167
elessair 0:f269e3021894 168 Re-flash the device and press the reset button. The device LED should be blinking as in the previous case.
elessair 0:f269e3021894 169
elessair 0:f269e3021894 170 ---
elessair 0:f269e3021894 171
elessair 0:f269e3021894 172 If you enable uVisor in the `blinky` app as it was written above, you will not get any particular security feature. All code and resources share the same security context, which we call the *main box*.
elessair 0:f269e3021894 173
elessair 0:f269e3021894 174 A lot happens under the hood, though. All the user code now runs in unprivileged mode, and the systems services like the `NVIC` APIs or the OS SVCalls are routed through the uVisor.
elessair 0:f269e3021894 175
elessair 0:f269e3021894 176 ## Add a secure box
elessair 0:f269e3021894 177 [Go to top](#overview)
elessair 0:f269e3021894 178
elessair 0:f269e3021894 179 Now that uVisor is enabled, we can finally add a *secure box*.
elessair 0:f269e3021894 180
elessair 0:f269e3021894 181 A secure box is a special compartment that is granted exclusive access to peripherals, memories and interrupts. Private resources are only accessible when the *context* of the secure box is active. The uVisor is the only one that can enable a secure box context, for example upon thread switching or interrupt handling.
elessair 0:f269e3021894 182
elessair 0:f269e3021894 183 Code that belongs to a box is not obfuscated by uVisor, so it is still readable and executable from outside of the box. In addition, declaring an object in the same file that configures a secure box does not protect that object automatically.
elessair 0:f269e3021894 184
elessair 0:f269e3021894 185 Instead, we provide specific APIs to instruct the uVisor to protect a private resource. Here we will show how to use these APIs in the `uvisor-example` app.
elessair 0:f269e3021894 186
elessair 0:f269e3021894 187 ### Configure the secure box
elessair 0:f269e3021894 188
elessair 0:f269e3021894 189 For this example, we want to create a secure box called `private_timer`. The `private_timer` box will be configured to have exclusive access to the PIT timer and to the GPIO PORT C on the NXP FRDM-K64F board, which means that other boxes will be prevented from accessing these peripherals.
elessair 0:f269e3021894 190
elessair 0:f269e3021894 191 Each secure box must have at least one thread, which we call the box's main thread. In our `private_timer` box we will only use this thread throughout the whole program. The thread will constantly save the current timer value in a private buffer. In addition, we want to print the content of the buffered timer values whenever we press the `SW2` button on the board.
elessair 0:f269e3021894 192
elessair 0:f269e3021894 193 We want the box to have exclusive access to the following resources:
elessair 0:f269e3021894 194
elessair 0:f269e3021894 195 * The timer and push-button peripherals (as specified by a peripheral ACL). Nobody else should be able to read the timer values.
elessair 0:f269e3021894 196 * The push-button interrupt (as specified by an IRQ ACL). We want the button IRQ to be re-routed to our box-specific ISR.
elessair 0:f269e3021894 197 * The buffer that holds the timer samples (as specified by a dynamic memory ACL).
elessair 0:f269e3021894 198 * The static memory that holds information about the timer buffer (as specified by a static memory ACL).
elessair 0:f269e3021894 199
elessair 0:f269e3021894 200 Create a new source file, `~/code/uvisor-example/source/secure_box.cpp`. We will configure the secure box inside this file. The secure box name for this example is `private_timer`.
elessair 0:f269e3021894 201
elessair 0:f269e3021894 202 ```C
elessair 0:f269e3021894 203 /* ~/code/uvisor-example/source/secure_box.cpp */
elessair 0:f269e3021894 204
elessair 0:f269e3021894 205 #include "mbed.h"
elessair 0:f269e3021894 206 #include "rtos.h"
elessair 0:f269e3021894 207 #include "uvisor-lib/uvisor-lib.h"
elessair 0:f269e3021894 208
elessair 0:f269e3021894 209 /* Private static memory for the secure box */
elessair 0:f269e3021894 210 typedef struct {
elessair 0:f269e3021894 211 uint32_t * buffer;
elessair 0:f269e3021894 212 int index;
elessair 0:f269e3021894 213 } PrivateTimerStaticMemory;
elessair 0:f269e3021894 214
elessair 0:f269e3021894 215 /* ACLs list for the secure box: Timer (PIT). */
elessair 0:f269e3021894 216 static const UvisorBoxAclItem g_private_timer_acls[] = {
elessair 0:f269e3021894 217 {PIT, sizeof(*PIT), UVISOR_TACLDEF_PERIPH}
elessair 0:f269e3021894 218 {PORTC, sizeof(*PORTC), UVISOR_TACLDEF_PERIPH},
elessair 0:f269e3021894 219 };
elessair 0:f269e3021894 220
elessair 0:f269e3021894 221 static void private_timer_main_thread(const void *);
elessair 0:f269e3021894 222
elessair 0:f269e3021894 223 /* Secure box configuration */
elessair 0:f269e3021894 224 UVISOR_BOX_NAMESPACE(NULL); /* We won't specify a box namespace for this example. */
elessair 0:f269e3021894 225 UVISOR_BOX_HEAPSIZE(4096); /* Heap size for the secure box */
elessair 0:f269e3021894 226 UVISOR_BOX_MAIN(private_timer_main_thread, /* Main thread for the secure box */
elessair 0:f269e3021894 227 osPriorityNormal, /* Priority of the secure box's main thread */
elessair 0:f269e3021894 228 1024); /* Stack size for the secure box's main thread */
elessair 0:f269e3021894 229 UVISOR_BOX_CONFIG(private_timer, /* Name of the secure box */
elessair 0:f269e3021894 230 g_private_timer_acls, /* ACLs list for the secure box */
elessair 0:f269e3021894 231 1024, /* Stack size for the secure box */
elessair 0:f269e3021894 232 PrivateTimerStaticMemory); /* Private static memory for the secure box. */
elessair 0:f269e3021894 233 ```
elessair 0:f269e3021894 234
elessair 0:f269e3021894 235 ### Create the secure box's main thread function
elessair 0:f269e3021894 236
elessair 0:f269e3021894 237 In general, you can decide what to do in your box's main thread. You can run it once and then kill it, use it to configure memories, peripherals, or to create other threads. In this app, the box's main thread is the only thread for the `private_timer` box, and it will run throughout the whole program.
elessair 0:f269e3021894 238
elessair 0:f269e3021894 239 The `private_timer_main_thread` function configures the PIT timer, allocates the dynamic buffer to hold the timer values and initializes its private static memory, `PrivateTimerStaticMemory`. A spinning loop is used to update the values in the buffer every time the thread is reactivated.
elessair 0:f269e3021894 240
elessair 0:f269e3021894 241 ```C
elessair 0:f269e3021894 242 /* Number of timer samples we will use */
elessair 0:f269e3021894 243 #define PRIVATE_TIMER_BUFFER_COUNT 256
elessair 0:f269e3021894 244
elessair 0:f269e3021894 245 /* For debug purposes: print the buffer values when the SW2 button is pressed. */
elessair 0:f269e3021894 246 static void private_timer_button_on_press(void)
elessair 0:f269e3021894 247 {
elessair 0:f269e3021894 248 for (int i = 0; i < PRIVATE_TIMER_BUFFER_COUNT; i++) {
elessair 0:f269e3021894 249 printf("buffer[%03d] = %lu\r\n", i, uvisor_ctx->buffer[i]);
elessair 0:f269e3021894 250 }
elessair 0:f269e3021894 251 }
elessair 0:f269e3021894 252
elessair 0:f269e3021894 253 /* Main thread for the secure box */
elessair 0:f269e3021894 254 static void private_timer_main_thread(const void *)
elessair 0:f269e3021894 255 {
elessair 0:f269e3021894 256 /* Create the buffer and cache its pointer to the private static memory. */
elessair 0:f269e3021894 257 uvisor_ctx->buffer = (uint32_t *) malloc(PRIVATE_TIMER_BUFFER_COUNT * sizeof(uint32_t));
elessair 0:f269e3021894 258 if (uvisor_ctx->buffer == NULL) {
elessair 0:f269e3021894 259 mbed_die();
elessair 0:f269e3021894 260 }
elessair 0:f269e3021894 261 uvisor_ctx->index = 0;
elessair 0:f269e3021894 262
elessair 0:f269e3021894 263 /* Setup the push-button callback. */
elessair 0:f269e3021894 264 InterruptIn button(SW2);
elessair 0:f269e3021894 265 button.mode(PullUp);
elessair 0:f269e3021894 266 button.fall(&private_timer_button_on_press);
elessair 0:f269e3021894 267
elessair 0:f269e3021894 268 /* Setup and start the timer. */
elessair 0:f269e3021894 269 Timer timer;
elessair 0:f269e3021894 270 timer.start();
elessair 0:f269e3021894 271
elessair 0:f269e3021894 272 while (1) {
elessair 0:f269e3021894 273 /* Store the timer value. */
elessair 0:f269e3021894 274 uvisor_ctx->buffer[uvisor_ctx->index] = timer.read_us();
elessair 0:f269e3021894 275
elessair 0:f269e3021894 276 /* Update the index. Behave as a circular buffer. */
elessair 0:f269e3021894 277 if (uvisor_ctx->index < PRIVATE_TIMER_BUFFER_COUNT - 1) {
elessair 0:f269e3021894 278 uvisor_ctx->index++;
elessair 0:f269e3021894 279 } else {
elessair 0:f269e3021894 280 uvisor_ctx->index = 0;
elessair 0:f269e3021894 281 }
elessair 0:f269e3021894 282 }
elessair 0:f269e3021894 283 }
elessair 0:f269e3021894 284 ```
elessair 0:f269e3021894 285
elessair 0:f269e3021894 286 A few things to note in the code above:
elessair 0:f269e3021894 287
elessair 0:f269e3021894 288 * If code is running in the context of `private_timer`, then any object instantiated inside that code will belong to the `private_timer` heap and stack. This means that in the example above the `InterruptIn` and `Timer` objects are private to the `private_timer` box. The same applies to the dynamically allocated buffer `uvisor_ctx->buffer`.
elessair 0:f269e3021894 289 * The content of the private memory `PrivateTimerStaticMemory` can be accessed using the `PrivateTimerStaticMemory * uvisor_ctx` pointer, which is maintained by uVisor.
elessair 0:f269e3021894 290 * The `InterruptIn` object triggers the registration of an interrupt slot. Since that code is run in the context of the `private_timer` box, then the push-button IRQ belongs to that box. If you want to use the IRQ APIs directly, read the [section](#the-nvic-apis) below.
elessair 0:f269e3021894 291 * Even if the `private_timer_button_on_press` function runs in the context of `private_timer`, we can still use the `printf` function, which accesses the `UART0` peripheral, owned by the main box. This is because all ACLs declared in the main box are by default shared with all the other secure boxes. This also means that the messages we are printing on the serial port are not secure, because other boxes have access to that peripheral.
elessair 0:f269e3021894 292
elessair 0:f269e3021894 293 > **Warning**: Instantiating an object in the `secure_box.cpp` global scope will automatically map it to the main box context, not the `private_timer` one. If you want an object to be private to a box, you need to instantiate it inside the code that will run in the context of that box (like the `InterruptIn` and `Timer` objects), or alternatively statically initialize it in the box private static memory (like the `buffer` and `index` variables in `PrivateTimerStaticMemory`).
elessair 0:f269e3021894 294
elessair 0:f269e3021894 295 ---
elessair 0:f269e3021894 296
elessair 0:f269e3021894 297 **Checkpoint**
elessair 0:f269e3021894 298
elessair 0:f269e3021894 299 Compile the application again:
elessair 0:f269e3021894 300
elessair 0:f269e3021894 301 ```bash
elessair 0:f269e3021894 302 $ mbed compile -m K64F -t GCC_ARM
elessair 0:f269e3021894 303 ```
elessair 0:f269e3021894 304
elessair 0:f269e3021894 305 Re-flash the device, and press the reset button. The device LED should be blinking as in the previous case.
elessair 0:f269e3021894 306
elessair 0:f269e3021894 307 If you don't see the LED blinking, it means that the application halted somewhere, probably because uVisor captured a fault. You can setup the uVisor debug messages to see if there is any problem. Follow the [Debugging uVisor on mbed OS](DEBUGGING.md) document for a step-by-step guide.
elessair 0:f269e3021894 308
elessair 0:f269e3021894 309 If the LED is blinking, it means that the app is running fine. If you now press the `SW2` button on the NXP FRDM-K64F board, the `private_timer_button_on_press` function will be executed, printing the values in the timer buffer. You can observe these values by opening a serial port connection to the device, with a baud rate of 9600. When the print is completed, you should see the LED blinking again.
elessair 0:f269e3021894 310
elessair 0:f269e3021894 311 ### Expose public secure entry points to the secure box
elessair 0:f269e3021894 312
elessair 0:f269e3021894 313 Coming soon.
elessair 0:f269e3021894 314
elessair 0:f269e3021894 315 ## Wrap-up
elessair 0:f269e3021894 316 [Go to top](#overview)
elessair 0:f269e3021894 317
elessair 0:f269e3021894 318 In this guide we showed you how to:
elessair 0:f269e3021894 319
elessair 0:f269e3021894 320 * Enable uVisor on an existing application.
elessair 0:f269e3021894 321 * Add a secure box to your application.
elessair 0:f269e3021894 322 * Protect static and dynamic memories in a secure box.
elessair 0:f269e3021894 323 * Gain exclusive access to a peripheral and an IRQ in a secure box.
elessair 0:f269e3021894 324 * (Coming soon) Expose public secure entry points to a secure box.
elessair 0:f269e3021894 325
elessair 0:f269e3021894 326 You can now modify the example or create a new one to protect your resources into a secure box. You might find the following resources useful:
elessair 0:f269e3021894 327
elessair 0:f269e3021894 328 * [uVisor API documentation](API.md)
elessair 0:f269e3021894 329 * [Debugging uVisor on mbed OS](DEBUGGING.md)
elessair 0:f269e3021894 330
elessair 0:f269e3021894 331 If you found any bug or inconsistency in this guide, please [raise an issue](https://github.com/ARMmbed/uvisor/issues/new).
elessair 0:f269e3021894 332
elessair 0:f269e3021894 333 ## Appendix
elessair 0:f269e3021894 334 [Go to top](#overview)
elessair 0:f269e3021894 335
elessair 0:f269e3021894 336 This section contains additional information that you might find useful when setting up a secure box.
elessair 0:f269e3021894 337
elessair 0:f269e3021894 338 ### The NVIC APIs
elessair 0:f269e3021894 339
elessair 0:f269e3021894 340 The ARM CMSIS header files provide APIs to configure, enable and disable IRQs in the NVIC module. These APIs are all prefixed with `NVIC_` and can be found in the `core_cm*.h` files in your CMSIS module.
elessair 0:f269e3021894 341
elessair 0:f269e3021894 342 In addition, the CMSIS header also provide APIs to set and get an interrupt vector at runtime. This requires the interrupt vector table, which is usually located in flash, to be relocated to SRAM.
elessair 0:f269e3021894 343
elessair 0:f269e3021894 344 When the uVisor is enabled, all NVIC APIs are re-routed to the corresponding uVisor vIRQ APIs, which virtualize the interrupt module. The uVisor interrupt model has the following features:
elessair 0:f269e3021894 345
elessair 0:f269e3021894 346 * The uVisor owns the interrupt vector table.
elessair 0:f269e3021894 347 * All ISRs are relocated to SRAM.
elessair 0:f269e3021894 348 * Code in a box can only change the state of an IRQ (enable it, change its priority, etc.) if the box registered that IRQ with uVisor at runtime, using the `NVIC_SetVector` API.
elessair 0:f269e3021894 349 * An IRQ that belongs to a box can only be modified when that box context is active.
elessair 0:f269e3021894 350
elessair 0:f269e3021894 351 Although this behaviour is different from the original NVIC one, it is backwards compatible. This means that legacy code (like a device HAL) will still work after uVisor is enabled. The general use case is the following:
elessair 0:f269e3021894 352
elessair 0:f269e3021894 353 ```C
elessair 0:f269e3021894 354 #define MY_IRQ 42
elessair 0:f269e3021894 355
elessair 0:f269e3021894 356 /* Set the ISR for MY_IRQ at runtime.
elessair 0:f269e3021894 357 * Without uVisor: Relocate the interrupt vector table to SRAM and set my_isr as
elessair 0:f269e3021894 358 the ISR for MY_IRQ.
elessair 0:f269e3021894 359 * With uVisor: Register MY_IRQ for the current box with my_isr as ISR. */
elessair 0:f269e3021894 360 NVIC_SetVector(MY_IRQ, &my_isr);
elessair 0:f269e3021894 361
elessair 0:f269e3021894 362 /* Change the IRQ state. */
elessair 0:f269e3021894 363 NVIC_SetPriority(MY_IRQ, 3);
elessair 0:f269e3021894 364 NVIC_EnableIRQ(MY_IRQ);
elessair 0:f269e3021894 365 ```
elessair 0:f269e3021894 366
elessair 0:f269e3021894 367 > **Note**: In this model a call to `NVIC_SetVector` must always happen before an IRQ state is changed. In platforms that don't relocate the interrupt vector table such a call might be originally absent and must be added to work with uVisor.
elessair 0:f269e3021894 368
elessair 0:f269e3021894 369 For more information on the uVisor APIs, checkout the [uVisor API documentation](API.md) document.
elessair 0:f269e3021894 370
elessair 0:f269e3021894 371 ### The *main box* ACLs
elessair 0:f269e3021894 372
elessair 0:f269e3021894 373 The code samples that we provide in this guide give you a ready-made list of ACLs for the main box. The list includes peripherals that we already know will be necessary to make the example app work, and it is specific to the NXP FRDM-K64F target.
elessair 0:f269e3021894 374
elessair 0:f269e3021894 375 This section shows how to discover the needed ACLs for the main box. You might need to follow these instructions in case you want to generate the ACLs list for a different target or a different app.
elessair 0:f269e3021894 376
elessair 0:f269e3021894 377 At the moment the uVisor does not provide a way to detect and list all the faulting ACLs for a given platform automatically. This is a planned feature that will be released in the future.
elessair 0:f269e3021894 378
elessair 0:f269e3021894 379 In order to generate the list of ACLs, use the code provided in the [Enable uVisor](#enable-uvisor) section. In this case, though, start with an empty ACLs list:
elessair 0:f269e3021894 380
elessair 0:f269e3021894 381 ```C
elessair 0:f269e3021894 382 static const UvisorBoxAclItem g_main_box_acls[] = {
elessair 0:f269e3021894 383 }
elessair 0:f269e3021894 384 ```
elessair 0:f269e3021894 385
elessair 0:f269e3021894 386 You now need to compile your application using uVisor in debug mode. This operation requires some more advanced steps, which are described in detail in the [Debugging uVisor on mbed OS](DEBUGGING.md) document. The main idea is that you compile the application in debug mode:
elessair 0:f269e3021894 387
elessair 0:f269e3021894 388 ```bash
elessair 0:f269e3021894 389 $ mbed compile -m K64F -t GCC_ARM -o "debug-info"
elessair 0:f269e3021894 390 ```
elessair 0:f269e3021894 391
elessair 0:f269e3021894 392 and then use a GDB-compatible interface to flash the device, enable semihosting, and access the uVisor debug messages. Please read the [Debugging uVisor on mbed OS](DEBUGGING.md) document for the detailed instructions.
elessair 0:f269e3021894 393
elessair 0:f269e3021894 394 Once the uVisor debug messages are enabled, you will see you application fail. The failure is due to the first missing ACL being hit by the main box code. The message will look like:
elessair 0:f269e3021894 395
elessair 0:f269e3021894 396 ```
elessair 0:f269e3021894 397 ***********************************************************
elessair 0:f269e3021894 398 BUS FAULT
elessair 0:f269e3021894 399 ***********************************************************
elessair 0:f269e3021894 400
elessair 0:f269e3021894 401 ...
elessair 0:f269e3021894 402
elessair 0:f269e3021894 403 * MEMORY MAP
elessair 0:f269e3021894 404 Address: 0x4004800C
elessair 0:f269e3021894 405 Region/Peripheral: SIM
elessair 0:f269e3021894 406 Base address: 0x40047000
elessair 0:f269e3021894 407 End address: 0x40048060
elessair 0:f269e3021894 408
elessair 0:f269e3021894 409 ...
elessair 0:f269e3021894 410 ```
elessair 0:f269e3021894 411
elessair 0:f269e3021894 412 Now that you know which peripheral is causing the fault (the `SIM` peripheral, in this example), you can add its entry to the ACLs list:
elessair 0:f269e3021894 413
elessair 0:f269e3021894 414
elessair 0:f269e3021894 415 ```C
elessair 0:f269e3021894 416 static const UvisorBoxAclItem g_main_box_acls[] = {
elessair 0:f269e3021894 417 {SIM, sizeof(*SIM), UVISOR_TACLDEF_PERIPH},
elessair 0:f269e3021894 418 };
elessair 0:f269e3021894 419 ```
elessair 0:f269e3021894 420
elessair 0:f269e3021894 421 > **Note**: If the fault debug screen does not show the name of the peripheral, you need to look it up in the target device reference manual.
elessair 0:f269e3021894 422
elessair 0:f269e3021894 423 For readability, do not use the hard-coded addresses of your peripherals, but rather use the symbols provided by the target CMSIS module.
elessair 0:f269e3021894 424
elessair 0:f269e3021894 425 Repeat the process multiple times until all ACLs have been added to the list. When no other ACL is needed any more, the system will run without hitting a uVisor fault.