Rtos API example

Committer:
marcozecchini
Date:
Sat Feb 23 12:13:36 2019 +0000
Revision:
0:9fca2b23d0ba
final commit

Who changed what in which revision?

UserRevisionLine numberNew contents of line
marcozecchini 0:9fca2b23d0ba 1 # Getting started guide for uVisor on mbed OS
marcozecchini 0:9fca2b23d0ba 2
marcozecchini 0:9fca2b23d0ba 3 This guide will help you start uVisor on mbed OS by showing you how to create a sample application for the NXP FRDM-K64F board.
marcozecchini 0:9fca2b23d0ba 4
marcozecchini 0:9fca2b23d0ba 5 The uVisor provides sandboxed environments and resources protection for applications built for ARM Cortex-M3 and Cortex-M4 devices. This guide will show you how to enable the uVisor and configure a secure box to access some exclusive resources (memory, peripherals, interrupts). For more information about the uVisor design philosophy, please see the uVisor [introductory document](../../README.md).
marcozecchini 0:9fca2b23d0ba 6
marcozecchini 0:9fca2b23d0ba 7 ## Requirements
marcozecchini 0:9fca2b23d0ba 8
marcozecchini 0:9fca2b23d0ba 9 To run the `blinky` application on mbed OS with uVisor enabled, you need:
marcozecchini 0:9fca2b23d0ba 10
marcozecchini 0:9fca2b23d0ba 11 - A platform and a toolchain that uVisor on mbed OS supports. You can verify this on [the official list](../README.md#supported-platforms). If uVisor supports your platform internally but not on mbed OS, the porting process is incomplete. To port your platform to uVisor and enable it on mbed OS, please follow the [uVisor porting guide for mbed OS](../core/PORTING.md).
marcozecchini 0:9fca2b23d0ba 12 - Git.
marcozecchini 0:9fca2b23d0ba 13 - mbed CLI. Run `pip install mbed-cli` to install it.
marcozecchini 0:9fca2b23d0ba 14
marcozecchini 0:9fca2b23d0ba 15 The remainder of this guide assumes:
marcozecchini 0:9fca2b23d0ba 16
marcozecchini 0:9fca2b23d0ba 17 - You are developing on a \*nix machine in the `~/code` folder.
marcozecchini 0:9fca2b23d0ba 18 - You are building the app for the [NXP FRDM-K64F](http://developer.mbed.org/platforms/FRDM-K64F/) target with the [GNU ARM Embedded Toolchain](https://launchpad.net/gcc-arm-embedded).
marcozecchini 0:9fca2b23d0ba 19
marcozecchini 0:9fca2b23d0ba 20 You can use these instructions as guidelines in the case of other targets on other host OSs.
marcozecchini 0:9fca2b23d0ba 21
marcozecchini 0:9fca2b23d0ba 22 ## Start with the `blinky` app
marcozecchini 0:9fca2b23d0ba 23 [Go to top](#overview)
marcozecchini 0:9fca2b23d0ba 24
marcozecchini 0:9fca2b23d0ba 25 Create a new mbed application called `uvisor-example` by running the following commands:
marcozecchini 0:9fca2b23d0ba 26
marcozecchini 0:9fca2b23d0ba 27 ```bash
marcozecchini 0:9fca2b23d0ba 28 $ cd ~/code
marcozecchini 0:9fca2b23d0ba 29 $ mbed new uvisor-example
marcozecchini 0:9fca2b23d0ba 30 $ cd uvisor-example
marcozecchini 0:9fca2b23d0ba 31 ```
marcozecchini 0:9fca2b23d0ba 32
marcozecchini 0:9fca2b23d0ba 33 The mbed CLI tools automatically fetch the mbed codebase. By default, Git tracks your code changes, so you can push your application to a Git server if you want to.
marcozecchini 0:9fca2b23d0ba 34
marcozecchini 0:9fca2b23d0ba 35 Once the import process finishes, create a `source` folder:
marcozecchini 0:9fca2b23d0ba 36 ```bash
marcozecchini 0:9fca2b23d0ba 37 $ mkdir ~/code/uvisor-example/source
marcozecchini 0:9fca2b23d0ba 38 ```
marcozecchini 0:9fca2b23d0ba 39 Place a new file `main.cpp` in it:
marcozecchini 0:9fca2b23d0ba 40
marcozecchini 0:9fca2b23d0ba 41 ```C
marcozecchini 0:9fca2b23d0ba 42 /* ~/code/uvisor-example/source/main.cpp */
marcozecchini 0:9fca2b23d0ba 43
marcozecchini 0:9fca2b23d0ba 44 #include "mbed.h"
marcozecchini 0:9fca2b23d0ba 45
marcozecchini 0:9fca2b23d0ba 46 DigitalOut led(LED1);
marcozecchini 0:9fca2b23d0ba 47
marcozecchini 0:9fca2b23d0ba 48 int main(void)
marcozecchini 0:9fca2b23d0ba 49 {
marcozecchini 0:9fca2b23d0ba 50 while (true) {
marcozecchini 0:9fca2b23d0ba 51 led = !led;
marcozecchini 0:9fca2b23d0ba 52 wait(0.5);
marcozecchini 0:9fca2b23d0ba 53 }
marcozecchini 0:9fca2b23d0ba 54 }
marcozecchini 0:9fca2b23d0ba 55 ```
marcozecchini 0:9fca2b23d0ba 56
marcozecchini 0:9fca2b23d0ba 57 This application blinks an LED from the main thread, which the OS creates by default.
marcozecchini 0:9fca2b23d0ba 58
marcozecchini 0:9fca2b23d0ba 59 ---
marcozecchini 0:9fca2b23d0ba 60
marcozecchini 0:9fca2b23d0ba 61 **Checkpoint**
marcozecchini 0:9fca2b23d0ba 62
marcozecchini 0:9fca2b23d0ba 63 Compile the application:
marcozecchini 0:9fca2b23d0ba 64
marcozecchini 0:9fca2b23d0ba 65 ```bash
marcozecchini 0:9fca2b23d0ba 66 $ mbed compile -m K64F -t GCC_ARM
marcozecchini 0:9fca2b23d0ba 67 ```
marcozecchini 0:9fca2b23d0ba 68
marcozecchini 0:9fca2b23d0ba 69 The resulting binary is located at:
marcozecchini 0:9fca2b23d0ba 70
marcozecchini 0:9fca2b23d0ba 71 ```bash
marcozecchini 0:9fca2b23d0ba 72 ~/code/uvisor-example/BUILD/K64F/GCC_ARM/uvisor-example.bin
marcozecchini 0:9fca2b23d0ba 73 ```
marcozecchini 0:9fca2b23d0ba 74
marcozecchini 0:9fca2b23d0ba 75 Drag and drop it onto the USB device mounted on your computer to flash the device. When the flashing process is complete, press the reset button on the device. The device's LED blinks.
marcozecchini 0:9fca2b23d0ba 76
marcozecchini 0:9fca2b23d0ba 77 ## Enable uVisor
marcozecchini 0:9fca2b23d0ba 78 [Go to top](#overview)
marcozecchini 0:9fca2b23d0ba 79
marcozecchini 0:9fca2b23d0ba 80 To enable the uVisor on the app, add these lines to the beginning of the `main.cpp` file:
marcozecchini 0:9fca2b23d0ba 81
marcozecchini 0:9fca2b23d0ba 82 ```C
marcozecchini 0:9fca2b23d0ba 83 /* ~/code/uvisor-example/source/main.cpp */
marcozecchini 0:9fca2b23d0ba 84
marcozecchini 0:9fca2b23d0ba 85 #include "mbed.h"
marcozecchini 0:9fca2b23d0ba 86 #include "uvisor-lib/uvisor-lib.h"
marcozecchini 0:9fca2b23d0ba 87
marcozecchini 0:9fca2b23d0ba 88 /* Public box Access Control Lists (ACLs). */
marcozecchini 0:9fca2b23d0ba 89 /* Note: These are specific to the NXP FRDM-K64F board. See the section below
marcozecchini 0:9fca2b23d0ba 90 * for more information. */
marcozecchini 0:9fca2b23d0ba 91 static const UvisorBoxAclItem g_public_box_acls[] = {
marcozecchini 0:9fca2b23d0ba 92 /* For the LED */
marcozecchini 0:9fca2b23d0ba 93 {SIM, sizeof(*SIM), UVISOR_TACLDEF_PERIPH},
marcozecchini 0:9fca2b23d0ba 94 {PORTB, sizeof(*PORTB), UVISOR_TACLDEF_PERIPH},
marcozecchini 0:9fca2b23d0ba 95
marcozecchini 0:9fca2b23d0ba 96 /* For messages printed on the serial port */
marcozecchini 0:9fca2b23d0ba 97 {OSC, sizeof(*OSC), UVISOR_TACLDEF_PERIPH},
marcozecchini 0:9fca2b23d0ba 98 {MCG, sizeof(*MCG), UVISOR_TACLDEF_PERIPH},
marcozecchini 0:9fca2b23d0ba 99 {UART0, sizeof(*UART0), UVISOR_TACLDEF_PERIPH},
marcozecchini 0:9fca2b23d0ba 100 {PIT, sizeof(*PIT), UVISOR_TACLDEF_PERIPH},
marcozecchini 0:9fca2b23d0ba 101 };
marcozecchini 0:9fca2b23d0ba 102
marcozecchini 0:9fca2b23d0ba 103 /* Enable uVisor, using the ACLs we just created. */
marcozecchini 0:9fca2b23d0ba 104 UVISOR_SET_MODE_ACL(UVISOR_ENABLED, g_public_box_acls);
marcozecchini 0:9fca2b23d0ba 105
marcozecchini 0:9fca2b23d0ba 106 /* Rest of the existing code */
marcozecchini 0:9fca2b23d0ba 107 ...
marcozecchini 0:9fca2b23d0ba 108 ```
marcozecchini 0:9fca2b23d0ba 109
marcozecchini 0:9fca2b23d0ba 110 In the code above, we specified two elements:
marcozecchini 0:9fca2b23d0ba 111
marcozecchini 0:9fca2b23d0ba 112 1. Public box Access Control Lists (ACLs). With uVisor enabled, everything runs in unprivileged mode, so make sure the public box and peripherals the OS accesses are allowed. These peripherals are specified using a list like the one in the snippet above. This example provides the list of all the ACLs you need. For other platforms or other applications, you need to determine those ACLs following the process in [The main box ACLs](#the-main-box-acls).
marcozecchini 0:9fca2b23d0ba 113 1. App-specific uVisor configurations: `UVISOR_SET_MODE_ACL`. This macro sets the uVisor mode (enabled) and associates the list of ACLs you just created with the public box.
marcozecchini 0:9fca2b23d0ba 114
marcozecchini 0:9fca2b23d0ba 115 Before compiling, you 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:
marcozecchini 0:9fca2b23d0ba 116
marcozecchini 0:9fca2b23d0ba 117 ```JSON
marcozecchini 0:9fca2b23d0ba 118 {
marcozecchini 0:9fca2b23d0ba 119 "target_overrides": {
marcozecchini 0:9fca2b23d0ba 120 "*": {
marcozecchini 0:9fca2b23d0ba 121 "target.features_add": ["UVISOR"],
marcozecchini 0:9fca2b23d0ba 122 "target.extra_labels_add": ["UVISOR_SUPPORTED"]
marcozecchini 0:9fca2b23d0ba 123 }
marcozecchini 0:9fca2b23d0ba 124 },
marcozecchini 0:9fca2b23d0ba 125 "macros": [
marcozecchini 0:9fca2b23d0ba 126 "FEATURE_UVISOR=1",
marcozecchini 0:9fca2b23d0ba 127 "TARGET_UVISOR_SUPPORTED=1"
marcozecchini 0:9fca2b23d0ba 128 ]
marcozecchini 0:9fca2b23d0ba 129 }
marcozecchini 0:9fca2b23d0ba 130 ```
marcozecchini 0:9fca2b23d0ba 131
marcozecchini 0:9fca2b23d0ba 132 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. Because the uVisor relies on those symbols in some assembly code, you need to define them manually.
marcozecchini 0:9fca2b23d0ba 133
marcozecchini 0:9fca2b23d0ba 134 ---
marcozecchini 0:9fca2b23d0ba 135
marcozecchini 0:9fca2b23d0ba 136 **Checkpoint**
marcozecchini 0:9fca2b23d0ba 137
marcozecchini 0:9fca2b23d0ba 138 Compile the application again. This time, the `K64F` target includes the new features and labels you provided in `mbed_app.json`;
marcozecchini 0:9fca2b23d0ba 139
marcozecchini 0:9fca2b23d0ba 140 ```bash
marcozecchini 0:9fca2b23d0ba 141 $ mbed compile -m K64F -t GCC_ARM
marcozecchini 0:9fca2b23d0ba 142 ```
marcozecchini 0:9fca2b23d0ba 143
marcozecchini 0:9fca2b23d0ba 144 The binary is located at:
marcozecchini 0:9fca2b23d0ba 145
marcozecchini 0:9fca2b23d0ba 146 ```bash
marcozecchini 0:9fca2b23d0ba 147 ~/code/uvisor-example/BUILD/K64F/GCC_ARM/uvisor-example.bin
marcozecchini 0:9fca2b23d0ba 148 ```
marcozecchini 0:9fca2b23d0ba 149
marcozecchini 0:9fca2b23d0ba 150 Reflash the device, and press the reset button. The device LED blinks as in the previous case.
marcozecchini 0:9fca2b23d0ba 151
marcozecchini 0:9fca2b23d0ba 152 ---
marcozecchini 0:9fca2b23d0ba 153
marcozecchini 0:9fca2b23d0ba 154 If you enable uVisor in the `blinky` app as it was written above, you do not get any particular security feature. All code and resources share the same security context, which we call the *public box*.
marcozecchini 0:9fca2b23d0ba 155
marcozecchini 0:9fca2b23d0ba 156 A lot happens unseen, though. All the user code now runs in unprivileged mode, and the systems services, such as the `NVIC` APIs and the OS SVCalls, are routed through the uVisor.
marcozecchini 0:9fca2b23d0ba 157
marcozecchini 0:9fca2b23d0ba 158 ## Add a secure box
marcozecchini 0:9fca2b23d0ba 159 [Go to top](#overview)
marcozecchini 0:9fca2b23d0ba 160
marcozecchini 0:9fca2b23d0ba 161 Now that uVisor is enabled, you can finally add a *secure box*.
marcozecchini 0:9fca2b23d0ba 162
marcozecchini 0:9fca2b23d0ba 163 A secure box is a special compartment with 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.
marcozecchini 0:9fca2b23d0ba 164
marcozecchini 0:9fca2b23d0ba 165 uVisor does not obfuscate code that belongs to a box, 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.
marcozecchini 0:9fca2b23d0ba 166
marcozecchini 0:9fca2b23d0ba 167 Instead, we provide specific APIs to instruct the uVisor to protect a private resource. The `uvisor-example` app will show how to use these APIs.
marcozecchini 0:9fca2b23d0ba 168
marcozecchini 0:9fca2b23d0ba 169 ### Configure the secure box
marcozecchini 0:9fca2b23d0ba 170
marcozecchini 0:9fca2b23d0ba 171 For this example, we want to create a secure box called `private_button`. The `private_button` box has exclusive access to the push-button on the NXP FRDM-K64F board, which means that other boxes cannot access its corresponding peripheral.
marcozecchini 0:9fca2b23d0ba 172
marcozecchini 0:9fca2b23d0ba 173 Each secure box must have at least one thread, which we call the box's main thread. In our `private_button` box, we only use this thread throughout the whole program. The thread runs every second and counts the number of times it has been called between two button presses. The thread count is saved in a variable private to the box. Whenever we press the `SW2` button on the board, the current thread count is stored into a private buffer and restarts. For debug purposes, the program prints the content of the buffer every time it fills up.
marcozecchini 0:9fca2b23d0ba 174
marcozecchini 0:9fca2b23d0ba 175 You want the box to have exclusive access to the following resources:
marcozecchini 0:9fca2b23d0ba 176
marcozecchini 0:9fca2b23d0ba 177 - The push-button peripheral (as specified by a peripheral ACL). Nobody else should be able to access the push-button port.
marcozecchini 0:9fca2b23d0ba 178 - The push-button interrupt (as specified by an IRQ ACL). You want the button IRQ to reroute to our box-specific ISR.
marcozecchini 0:9fca2b23d0ba 179 - The private dynamically allocated buffer (as specified by a dynamic memory ACL).
marcozecchini 0:9fca2b23d0ba 180 - The private variables (as specified by a static memory ACL).
marcozecchini 0:9fca2b23d0ba 181
marcozecchini 0:9fca2b23d0ba 182 Create a new source file, `~/code/uvisor-example/source/secure_box.cpp`. You will configure the secure box inside this file. The secure box name for this example is `private_button`.
marcozecchini 0:9fca2b23d0ba 183
marcozecchini 0:9fca2b23d0ba 184 ```C
marcozecchini 0:9fca2b23d0ba 185 /* ~/code/uvisor-example/source/secure_box.cpp */
marcozecchini 0:9fca2b23d0ba 186
marcozecchini 0:9fca2b23d0ba 187 #include "mbed.h"
marcozecchini 0:9fca2b23d0ba 188 #include "uvisor-lib/uvisor-lib.h"
marcozecchini 0:9fca2b23d0ba 189
marcozecchini 0:9fca2b23d0ba 190 /* Private static memory for the secure box */
marcozecchini 0:9fca2b23d0ba 191 typedef struct {
marcozecchini 0:9fca2b23d0ba 192 uint32_t * buffer; /* Static private memory, pointing to dynamically allocated private memory */
marcozecchini 0:9fca2b23d0ba 193 uint32_t counter; /* Static private memory */
marcozecchini 0:9fca2b23d0ba 194 int index; /* Static private memory */
marcozecchini 0:9fca2b23d0ba 195 RawSerial * pc; /* Static private memory, pointing to dynamically allocated private memory */
marcozecchini 0:9fca2b23d0ba 196 } PrivateButtonStaticMemory;
marcozecchini 0:9fca2b23d0ba 197
marcozecchini 0:9fca2b23d0ba 198 /* ACLs list for the secure box: Timer (PIT). */
marcozecchini 0:9fca2b23d0ba 199 static const UvisorBoxAclItem g_private_button_acls[] = {
marcozecchini 0:9fca2b23d0ba 200 {PORTC, sizeof(*PORTC), UVISOR_TACLDEF_PERIPH}, /* Private peripheral */
marcozecchini 0:9fca2b23d0ba 201 {(void *) PORTC_IRQn, 0, UVISOR_TACL_IRQ}, /* Private IRQ */
marcozecchini 0:9fca2b23d0ba 202 };
marcozecchini 0:9fca2b23d0ba 203
marcozecchini 0:9fca2b23d0ba 204 static void private_button_main_thread(const void *);
marcozecchini 0:9fca2b23d0ba 205
marcozecchini 0:9fca2b23d0ba 206 /* Secure box configuration */
marcozecchini 0:9fca2b23d0ba 207 UVISOR_BOX_NAMESPACE(NULL); /* We won't specify a box namespace for this example. */
marcozecchini 0:9fca2b23d0ba 208 UVISOR_BOX_HEAPSIZE(4096); /* Heap size for the secure box */
marcozecchini 0:9fca2b23d0ba 209 UVISOR_BOX_MAIN(private_button_main_thread, /* Main thread for the secure box */
marcozecchini 0:9fca2b23d0ba 210 osPriorityNormal, /* Priority of the secure box's main thread */
marcozecchini 0:9fca2b23d0ba 211 1024); /* Stack size for the secure box's main thread */
marcozecchini 0:9fca2b23d0ba 212 UVISOR_BOX_CONFIG(private_button, /* Name of the secure box */
marcozecchini 0:9fca2b23d0ba 213 g_private_button_acls, /* ACLs list for the secure box */
marcozecchini 0:9fca2b23d0ba 214 1024, /* Stack size for the secure box */
marcozecchini 0:9fca2b23d0ba 215 PrivateButtonStaticMemory); /* Private static memory for the secure box. */
marcozecchini 0:9fca2b23d0ba 216 ```
marcozecchini 0:9fca2b23d0ba 217
marcozecchini 0:9fca2b23d0ba 218 ### Create the secure box's main thread function
marcozecchini 0:9fca2b23d0ba 219
marcozecchini 0:9fca2b23d0ba 220 In general, you can decide what to do in your box's main thread. You can run it once and then stop it or use it to configure memories or peripherals or to create other threads. In this app, the box's main thread is the only thread for the `private_button` box, and it runs throughout the program.
marcozecchini 0:9fca2b23d0ba 221
marcozecchini 0:9fca2b23d0ba 222 The `private_button_main_thread` function configures the push-button to trigger an interrupt when pressed, allocates the dynamic buffer to hold the thread count values and initializes its private static memory, `PrivateButtonStaticMemory`. A spinning loop updates the counter value every second.
marcozecchini 0:9fca2b23d0ba 223
marcozecchini 0:9fca2b23d0ba 224 ```C
marcozecchini 0:9fca2b23d0ba 225 /* ~/code/uvisor-example/source/secure_box.cpp */
marcozecchini 0:9fca2b23d0ba 226
marcozecchini 0:9fca2b23d0ba 227 /* The previous code goes here. */
marcozecchini 0:9fca2b23d0ba 228 ...
marcozecchini 0:9fca2b23d0ba 229
marcozecchini 0:9fca2b23d0ba 230 #define uvisor_ctx ((PrivateButtonStaticMemory *) __uvisor_ctx)
marcozecchini 0:9fca2b23d0ba 231
marcozecchini 0:9fca2b23d0ba 232 #define PRIVATE_BUTTON_BUFFER_COUNT 8
marcozecchini 0:9fca2b23d0ba 233
marcozecchini 0:9fca2b23d0ba 234 static void private_button_on_press(void)
marcozecchini 0:9fca2b23d0ba 235 {
marcozecchini 0:9fca2b23d0ba 236 /* Store the thread count into the buffer and reset it. */
marcozecchini 0:9fca2b23d0ba 237 uvisor_ctx->buffer[uvisor_ctx->index] = uvisor_ctx->counter;
marcozecchini 0:9fca2b23d0ba 238 uvisor_ctx->counter = 0;
marcozecchini 0:9fca2b23d0ba 239
marcozecchini 0:9fca2b23d0ba 240 /* Update the index. Behave as a circular buffer. */
marcozecchini 0:9fca2b23d0ba 241 if (uvisor_ctx->index < PRIVATE_BUTTON_BUFFER_COUNT - 1) {
marcozecchini 0:9fca2b23d0ba 242 uvisor_ctx->index++;
marcozecchini 0:9fca2b23d0ba 243 } else {
marcozecchini 0:9fca2b23d0ba 244 uvisor_ctx->index = 0;
marcozecchini 0:9fca2b23d0ba 245
marcozecchini 0:9fca2b23d0ba 246 /* For debug purposes: Print the content of the buffer. */
marcozecchini 0:9fca2b23d0ba 247 uvisor_ctx->pc->printf("Thread count between button presses: ");
marcozecchini 0:9fca2b23d0ba 248 for (int i = 0; i < PRIVATE_BUTTON_BUFFER_COUNT; ++i) {
marcozecchini 0:9fca2b23d0ba 249 uvisor_ctx->pc->printf("%lu ", uvisor_ctx->buffer[i]);
marcozecchini 0:9fca2b23d0ba 250 }
marcozecchini 0:9fca2b23d0ba 251 uvisor_ctx->pc->printf("\n");
marcozecchini 0:9fca2b23d0ba 252 }
marcozecchini 0:9fca2b23d0ba 253
marcozecchini 0:9fca2b23d0ba 254 }
marcozecchini 0:9fca2b23d0ba 255
marcozecchini 0:9fca2b23d0ba 256 /* Main thread for the secure box */
marcozecchini 0:9fca2b23d0ba 257 static void private_button_main_thread(const void *)
marcozecchini 0:9fca2b23d0ba 258 {
marcozecchini 0:9fca2b23d0ba 259 /* Allocate serial port to ensure that code in this secure box
marcozecchini 0:9fca2b23d0ba 260 * won't touch handle in the default security context when printing */
marcozecchini 0:9fca2b23d0ba 261 if (!(uvisor_ctx->pc = new RawSerial(USBTX, USBRX))) {
marcozecchini 0:9fca2b23d0ba 262 return;
marcozecchini 0:9fca2b23d0ba 263 }
marcozecchini 0:9fca2b23d0ba 264
marcozecchini 0:9fca2b23d0ba 265 /* Create the buffer and cache its pointer to the private static memory. */
marcozecchini 0:9fca2b23d0ba 266 uvisor_ctx->buffer = (uint32_t *) malloc(PRIVATE_BUTTON_BUFFER_COUNT * sizeof(uint32_t));
marcozecchini 0:9fca2b23d0ba 267 if (uvisor_ctx->buffer == NULL) {
marcozecchini 0:9fca2b23d0ba 268 uvisor_ctx->pc->printf("ERROR: Failed to allocate memory for the button buffer\n");
marcozecchini 0:9fca2b23d0ba 269 mbed_die();
marcozecchini 0:9fca2b23d0ba 270 }
marcozecchini 0:9fca2b23d0ba 271 uvisor_ctx->index = 0;
marcozecchini 0:9fca2b23d0ba 272 uvisor_ctx->counter = 0;
marcozecchini 0:9fca2b23d0ba 273
marcozecchini 0:9fca2b23d0ba 274 /* Setup the push-button callback. */
marcozecchini 0:9fca2b23d0ba 275 InterruptIn button(SW2); /* Private IRQ */
marcozecchini 0:9fca2b23d0ba 276 button.mode(PullUp);
marcozecchini 0:9fca2b23d0ba 277 button.fall(&private_button_on_press);
marcozecchini 0:9fca2b23d0ba 278
marcozecchini 0:9fca2b23d0ba 279 /* Increment the private counter every second. */
marcozecchini 0:9fca2b23d0ba 280 while (1) {
marcozecchini 0:9fca2b23d0ba 281 uvisor_ctx->counter++;
marcozecchini 0:9fca2b23d0ba 282 wait(1.0);
marcozecchini 0:9fca2b23d0ba 283 }
marcozecchini 0:9fca2b23d0ba 284 }
marcozecchini 0:9fca2b23d0ba 285 ```
marcozecchini 0:9fca2b23d0ba 286
marcozecchini 0:9fca2b23d0ba 287 A few things to note in the code above:
marcozecchini 0:9fca2b23d0ba 288
marcozecchini 0:9fca2b23d0ba 289 - If code runs in the context of `private_button`, then any object instantiated inside that code belongs to the `private_button` heap and stack. This means that in the example above, the `InterruptIn` object is private to the `private_button` box. The same applies to the dynamically allocated buffer `uvisor_ctx->buffer`.
marcozecchini 0:9fca2b23d0ba 290 - You can access the content of the private memory `PrivateButtonStaticMemory` using the `void * const __uvisor_ctx` pointer, which uVisor maintains. You need to cast this pointer to your own context type. In this example we used a pre-processor symbol to improve readability.
marcozecchini 0:9fca2b23d0ba 291 - The `InterruptIn` object triggers the registration of an interrupt slot using the NVIC APIs. If you want to use the IRQ APIs directly, read the [NVIC APIs section](#the-nvic-apis) below. We registered the push-button IRQ to the `private_button` box through an IRQ ACL, and hence only code from this box can access it. Changing the push-button IRQ state from the public box causes a uVisor fault.
marcozecchini 0:9fca2b23d0ba 292 - Even if the `private_button_on_press` function runs in the context of `private_button`, you can still use the `printf` function, which accesses the `UART0` peripheral, owned by the public box. This is because all ACLs declared in the public 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.
marcozecchini 0:9fca2b23d0ba 293
marcozecchini 0:9fca2b23d0ba 294 > **Warning**: Instantiating an object in the `secure_box.cpp` global scope automatically maps it to the public box context, not the `private_button` one. If you want an object to be private to a box, you need to instantiate it inside the code that runs in the context of that box (such as the `InterruptIn` object), or alternatively statically initialize it in the box private static memory (such as the `buffer`, `index` and `counter` variables in `PrivateButtonStaticMemory`).
marcozecchini 0:9fca2b23d0ba 295
marcozecchini 0:9fca2b23d0ba 296 ---
marcozecchini 0:9fca2b23d0ba 297
marcozecchini 0:9fca2b23d0ba 298 **Checkpoint**
marcozecchini 0:9fca2b23d0ba 299
marcozecchini 0:9fca2b23d0ba 300 Compile the application again:
marcozecchini 0:9fca2b23d0ba 301
marcozecchini 0:9fca2b23d0ba 302 ```bash
marcozecchini 0:9fca2b23d0ba 303 $ mbed compile -m K64F -t GCC_ARM
marcozecchini 0:9fca2b23d0ba 304 ```
marcozecchini 0:9fca2b23d0ba 305
marcozecchini 0:9fca2b23d0ba 306 Reflash the device, and press the reset button. The device LED blinks.
marcozecchini 0:9fca2b23d0ba 307
marcozecchini 0:9fca2b23d0ba 308 If the LED doens't blink, it means the application halted somewhere, probably because uVisor captured a fault. You can set up the uVisor debug messages to see if there is a problem. See [Debugging uVisor on mbed OS](DEBUGGING.md) for a step-by-step guide.
marcozecchini 0:9fca2b23d0ba 309
marcozecchini 0:9fca2b23d0ba 310 If the LED is blinking, the app is running correctly. If you press the `SW2` button on the NXP FRDM-K64F board, the `private_button_on_press` function executes, printing the values in the timer buffer after `PRIVATE_BUTTON_BUFFER_COUNT` presses. You can observe these values by opening a serial port connection to the device, with a baud rate of 9600.
marcozecchini 0:9fca2b23d0ba 311
marcozecchini 0:9fca2b23d0ba 312 ## Expose public secure entry points to the secure box
marcozecchini 0:9fca2b23d0ba 313 [Go to top](#overview)
marcozecchini 0:9fca2b23d0ba 314
marcozecchini 0:9fca2b23d0ba 315 So far, the code in the secure box cannot communicate to other boxes. To let other boxes call functions in our secure box, you can define public secure entry points. These entry points can map to private functions within the context of a secure box, and an RPC protocol automatically serializes the arguments and return values to ensure no private memory can leak to external boxes.
marcozecchini 0:9fca2b23d0ba 316
marcozecchini 0:9fca2b23d0ba 317 You can define a public secure entry point to retrieve the index value from the secure box. This index value increases every time you press the `SW2` button.
marcozecchini 0:9fca2b23d0ba 318
marcozecchini 0:9fca2b23d0ba 319 ### Defining a secure entry point
marcozecchini 0:9fca2b23d0ba 320
marcozecchini 0:9fca2b23d0ba 321 Create a new source file, `~/code/uvisor-example/source/secure_box.h`, where you will define the functions that you can call through RPC.
marcozecchini 0:9fca2b23d0ba 322
marcozecchini 0:9fca2b23d0ba 323 ```cpp
marcozecchini 0:9fca2b23d0ba 324 /* ~/code/uvisor-example/source/secure_box.h */
marcozecchini 0:9fca2b23d0ba 325
marcozecchini 0:9fca2b23d0ba 326 #ifndef SECURE_BOX_H_
marcozecchini 0:9fca2b23d0ba 327 #define SECURE_BOX_H_
marcozecchini 0:9fca2b23d0ba 328
marcozecchini 0:9fca2b23d0ba 329 #include "uvisor-lib/uvisor-lib.h"
marcozecchini 0:9fca2b23d0ba 330
marcozecchini 0:9fca2b23d0ba 331 UVISOR_EXTERN int (*secure_get_index)(void);
marcozecchini 0:9fca2b23d0ba 332
marcozecchini 0:9fca2b23d0ba 333 #endif
marcozecchini 0:9fca2b23d0ba 334 ```
marcozecchini 0:9fca2b23d0ba 335
marcozecchini 0:9fca2b23d0ba 336 ### Implementing a secure entry point
marcozecchini 0:9fca2b23d0ba 337
marcozecchini 0:9fca2b23d0ba 338 Now that you have defined the secure entry point, you can map the entry point to a function running in the secure box. You can do this through the `UVISOR_BOX_RPC_GATEWAY_SYNC` macro. Open `~/code/uvisor-example/source/secure_box.cpp`, and replace the line with `#define PRIVATE_BUTTON_BUFFER_COUNT 8` by:
marcozecchini 0:9fca2b23d0ba 339
marcozecchini 0:9fca2b23d0ba 340 ```cpp
marcozecchini 0:9fca2b23d0ba 341 /* ~/code/uvisor-example/source/secure_box.cpp */
marcozecchini 0:9fca2b23d0ba 342
marcozecchini 0:9fca2b23d0ba 343 /* Function called through RPC */
marcozecchini 0:9fca2b23d0ba 344 static int get_index() {
marcozecchini 0:9fca2b23d0ba 345 /* Access to private memory here */
marcozecchini 0:9fca2b23d0ba 346 return uvisor_ctx->index;
marcozecchini 0:9fca2b23d0ba 347 }
marcozecchini 0:9fca2b23d0ba 348
marcozecchini 0:9fca2b23d0ba 349 UVISOR_BOX_RPC_GATEWAY_SYNC (private_button, secure_get_index, get_index, int, void);
marcozecchini 0:9fca2b23d0ba 350
marcozecchini 0:9fca2b23d0ba 351 #define PRIVATE_BUTTON_BUFFER_COUNT 8
marcozecchini 0:9fca2b23d0ba 352 ```
marcozecchini 0:9fca2b23d0ba 353
marcozecchini 0:9fca2b23d0ba 354 ### Listening for RPC messages
marcozecchini 0:9fca2b23d0ba 355
marcozecchini 0:9fca2b23d0ba 356 To receive RPC messages, you need to spin up a new thread, running in the secure box context. You can do this in the main thread of the secure box. In `~/code/uvisor-example/source/secure_box.cpp`, replace the first five lines of `private_button_main_thread` with:
marcozecchini 0:9fca2b23d0ba 357
marcozecchini 0:9fca2b23d0ba 358 ```cpp
marcozecchini 0:9fca2b23d0ba 359 /* ~/code/uvisor-example/source/secure_box.cpp */
marcozecchini 0:9fca2b23d0ba 360
marcozecchini 0:9fca2b23d0ba 361 static void listen_for_rpc() {
marcozecchini 0:9fca2b23d0ba 362 /* List of functions to wait for */
marcozecchini 0:9fca2b23d0ba 363 static const TFN_Ptr my_fn_array[] = {
marcozecchini 0:9fca2b23d0ba 364 (TFN_Ptr) get_index
marcozecchini 0:9fca2b23d0ba 365 };
marcozecchini 0:9fca2b23d0ba 366
marcozecchini 0:9fca2b23d0ba 367 while (1) {
marcozecchini 0:9fca2b23d0ba 368 int caller_id;
marcozecchini 0:9fca2b23d0ba 369 int status = rpc_fncall_waitfor(my_fn_array, 1, &caller_id, UVISOR_WAIT_FOREVER);
marcozecchini 0:9fca2b23d0ba 370
marcozecchini 0:9fca2b23d0ba 371 if (status) {
marcozecchini 0:9fca2b23d0ba 372 uvisor_error(USER_NOT_ALLOWED);
marcozecchini 0:9fca2b23d0ba 373 }
marcozecchini 0:9fca2b23d0ba 374 }
marcozecchini 0:9fca2b23d0ba 375 }
marcozecchini 0:9fca2b23d0ba 376
marcozecchini 0:9fca2b23d0ba 377 /* Main thread for the secure box */
marcozecchini 0:9fca2b23d0ba 378 static void private_button_main_thread(const void *)
marcozecchini 0:9fca2b23d0ba 379 {
marcozecchini 0:9fca2b23d0ba 380 /* allocate serial port to ensure that code in this secure box
marcozecchini 0:9fca2b23d0ba 381 * won't touch handle in the default security context when printing */
marcozecchini 0:9fca2b23d0ba 382 if (!(uvisor_ctx->pc = new RawSerial(USBTX, USBRX)))
marcozecchini 0:9fca2b23d0ba 383 return;
marcozecchini 0:9fca2b23d0ba 384
marcozecchini 0:9fca2b23d0ba 385 /* Start listening for RPC messages in a separate thread */
marcozecchini 0:9fca2b23d0ba 386 Thread rpc_thread(osPriorityNormal, 1024);
marcozecchini 0:9fca2b23d0ba 387 rpc_thread.start(&listen_for_rpc);
marcozecchini 0:9fca2b23d0ba 388
marcozecchini 0:9fca2b23d0ba 389 /* ... Rest of the private_button_main_thread function ... */
marcozecchini 0:9fca2b23d0ba 390 ```
marcozecchini 0:9fca2b23d0ba 391
marcozecchini 0:9fca2b23d0ba 392 ### Calling the public secure entry point
marcozecchini 0:9fca2b23d0ba 393
marcozecchini 0:9fca2b23d0ba 394 To call the public secure entry point from any other box, you can use the `secure_get_index` function. It will automatically do an RPC call into the secure box and serialize the return value. You can try this out from the public box. In `~/code/uvisor-example/source/main.cpp`, first include the header file for the secure box:
marcozecchini 0:9fca2b23d0ba 395
marcozecchini 0:9fca2b23d0ba 396 ```cpp
marcozecchini 0:9fca2b23d0ba 397 /* ~/code/uvisor-example/source/main.cpp */
marcozecchini 0:9fca2b23d0ba 398
marcozecchini 0:9fca2b23d0ba 399 #include "secure_box.h"
marcozecchini 0:9fca2b23d0ba 400 ```
marcozecchini 0:9fca2b23d0ba 401
marcozecchini 0:9fca2b23d0ba 402 Then replace the `main` function with:
marcozecchini 0:9fca2b23d0ba 403
marcozecchini 0:9fca2b23d0ba 404 ```cpp
marcozecchini 0:9fca2b23d0ba 405 /* ~/code/uvisor-example/source/main.cpp */
marcozecchini 0:9fca2b23d0ba 406
marcozecchini 0:9fca2b23d0ba 407 int main(void)
marcozecchini 0:9fca2b23d0ba 408 {
marcozecchini 0:9fca2b23d0ba 409 while (true) {
marcozecchini 0:9fca2b23d0ba 410 led = !led;
marcozecchini 0:9fca2b23d0ba 411 printf("Secure index is %d\n", secure_get_index());
marcozecchini 0:9fca2b23d0ba 412 Thread::wait(500);
marcozecchini 0:9fca2b23d0ba 413 }
marcozecchini 0:9fca2b23d0ba 414 }
marcozecchini 0:9fca2b23d0ba 415 ```
marcozecchini 0:9fca2b23d0ba 416
marcozecchini 0:9fca2b23d0ba 417 You can observe the secure index by opening a serial port connection to the device with a baud rate of 9600. When you press the `SW2` button, the index will increase.
marcozecchini 0:9fca2b23d0ba 418
marcozecchini 0:9fca2b23d0ba 419 ## The NVIC APIs
marcozecchini 0:9fca2b23d0ba 420
marcozecchini 0:9fca2b23d0ba 421 The ARM CMSIS header files provide APIs to configure, enable and disable IRQs in the NVIC module. These APIs all begin with `NVIC_`, and you can find them in the `core_cm*.h` files in your CMSIS module. The CMSIS header files also provide APIs to set and get an interrupt vector at runtime. This requires the relocation of the interrupt vector table, which is usually located in flash, to SRAM.
marcozecchini 0:9fca2b23d0ba 422
marcozecchini 0:9fca2b23d0ba 423 When the uVisor is enabled, all NVIC APIs are rerouted to the corresponding uVisor vIRQ APIs, which virtualize the interrupt module. The uVisor interrupt model has the following features:
marcozecchini 0:9fca2b23d0ba 424
marcozecchini 0:9fca2b23d0ba 425 - The uVisor owns the interrupt vector table.
marcozecchini 0:9fca2b23d0ba 426 - All ISRs are relocated to SRAM.
marcozecchini 0:9fca2b23d0ba 427 - Code in a box can only change the state of an IRQ (enable it, change its priority and so on) if the box registered that IRQ with uVisor through an IRQ ACL.
marcozecchini 0:9fca2b23d0ba 428 - An IRQ that belongs to a box can only be modified when that box context is active.
marcozecchini 0:9fca2b23d0ba 429
marcozecchini 0:9fca2b23d0ba 430 Although this behavior is different from that of the original NVIC, it is backward compatible. Legacy code (such as a device HAL) still works after uVisor is enabled.
marcozecchini 0:9fca2b23d0ba 431
marcozecchini 0:9fca2b23d0ba 432 All IRQ slots that are not listed in any box ACL list are considered unclaimed. Boxes can gain exclusive ownership of unclaimed IRQs on a first-come first-served basis through the use of the NVIC APIs.
marcozecchini 0:9fca2b23d0ba 433
marcozecchini 0:9fca2b23d0ba 434 ## The *public box* ACLs
marcozecchini 0:9fca2b23d0ba 435
marcozecchini 0:9fca2b23d0ba 436 The code samples in this guide provide a list of ACLs for the public box. The list includes peripherals necessary to make the example app work, and they are specific to the NXP FRDM-K64F target.
marcozecchini 0:9fca2b23d0ba 437
marcozecchini 0:9fca2b23d0ba 438 To generate the ACLs list for a different target or a different app, use the code provided in the [Enable uVisor](#enable-uvisor) section, but start with an empty ACLs list:
marcozecchini 0:9fca2b23d0ba 439
marcozecchini 0:9fca2b23d0ba 440 ```C
marcozecchini 0:9fca2b23d0ba 441 static const UvisorBoxAclItem g_public_box_acls[] = {
marcozecchini 0:9fca2b23d0ba 442 }
marcozecchini 0:9fca2b23d0ba 443 ```
marcozecchini 0:9fca2b23d0ba 444
marcozecchini 0:9fca2b23d0ba 445 Compile your application using uVisor in debug mode. This operation requires some more advanced steps. Please read [Debugging uVisor on mbed OS](DEBUGGING.md) for the detailed instructions.
marcozecchini 0:9fca2b23d0ba 446
marcozecchini 0:9fca2b23d0ba 447 Once the uVisor debug messages are enabled, your application fails. The failure is due to the first missing ACL being hit by the public box code. The message will look like:
marcozecchini 0:9fca2b23d0ba 448
marcozecchini 0:9fca2b23d0ba 449 ```
marcozecchini 0:9fca2b23d0ba 450 ***********************************************************
marcozecchini 0:9fca2b23d0ba 451 BUS FAULT
marcozecchini 0:9fca2b23d0ba 452 ***********************************************************
marcozecchini 0:9fca2b23d0ba 453
marcozecchini 0:9fca2b23d0ba 454 * Active Box ID: 0
marcozecchini 0:9fca2b23d0ba 455 * FAULT SYNDROME REGISTERS
marcozecchini 0:9fca2b23d0ba 456
marcozecchini 0:9fca2b23d0ba 457 CFSR: 0x00008200
marcozecchini 0:9fca2b23d0ba 458 BFAR: 0x40048044
marcozecchini 0:9fca2b23d0ba 459 --> PRECISERR: precise data access.
marcozecchini 0:9fca2b23d0ba 460
marcozecchini 0:9fca2b23d0ba 461 ...
marcozecchini 0:9fca2b23d0ba 462 ```
marcozecchini 0:9fca2b23d0ba 463
marcozecchini 0:9fca2b23d0ba 464 Look up the faulty address (the value of BFAR) in the target device reference manual.
marcozecchini 0:9fca2b23d0ba 465
marcozecchini 0:9fca2b23d0ba 466 Once you know which peripheral is causing the fault (the `SIM` peripheral, in this example), add its entry to the ACLs list:
marcozecchini 0:9fca2b23d0ba 467
marcozecchini 0:9fca2b23d0ba 468 ```C
marcozecchini 0:9fca2b23d0ba 469 static const UvisorBoxAclItem g_public_box_acls[] = {
marcozecchini 0:9fca2b23d0ba 470 {SIM, sizeof(*SIM), UVISOR_TACLDEF_PERIPH},
marcozecchini 0:9fca2b23d0ba 471 };
marcozecchini 0:9fca2b23d0ba 472 ```
marcozecchini 0:9fca2b23d0ba 473
marcozecchini 0:9fca2b23d0ba 474 > **Note**: If the fault debug screen does not show the name of the peripheral, look it up in the target device reference manual.
marcozecchini 0:9fca2b23d0ba 475
marcozecchini 0:9fca2b23d0ba 476 For readability, do not use the hard-coded addresses of your peripherals. Instead, use the symbols that the target CMSIS module provides.
marcozecchini 0:9fca2b23d0ba 477
marcozecchini 0:9fca2b23d0ba 478 Repeat the process multiple times until all ACLs have been added to the list. When no other ACL is needed, the system runs without hitting a uVisor fault.
marcozecchini 0:9fca2b23d0ba 479
marcozecchini 0:9fca2b23d0ba 480 ## Additional resources
marcozecchini 0:9fca2b23d0ba 481 [Go to top](#overview)
marcozecchini 0:9fca2b23d0ba 482
marcozecchini 0:9fca2b23d0ba 483 - [uVisor API documentation](API.md).
marcozecchini 0:9fca2b23d0ba 484 - [Debugging uVisor on mbed OS](DEBUGGING.md).
marcozecchini 0:9fca2b23d0ba 485 - [Using nonvolatile storage from uVisor on mbed OS](manual/Flash.md).
marcozecchini 0:9fca2b23d0ba 486
marcozecchini 0:9fca2b23d0ba 487 If you found any bug or inconsistency in this guide, please [raise an issue](https://github.com/ARMmbed/uvisor/issues/new).