BBR 1 Ebene

Committer:
borlanic
Date:
Mon May 14 11:29:06 2018 +0000
Revision:
0:fbdae7e6d805
BBR

Who changed what in which revision?

UserRevisionLine numberNew contents of line
borlanic 0:fbdae7e6d805 1 # Getting started guide for uVisor on mbed OS
borlanic 0:fbdae7e6d805 2
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 4
borlanic 0:fbdae7e6d805 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).
borlanic 0:fbdae7e6d805 6
borlanic 0:fbdae7e6d805 7 ## Requirements
borlanic 0:fbdae7e6d805 8
borlanic 0:fbdae7e6d805 9 To run the `blinky` application on mbed OS with uVisor enabled, you need:
borlanic 0:fbdae7e6d805 10
borlanic 0:fbdae7e6d805 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).
borlanic 0:fbdae7e6d805 12 - Git.
borlanic 0:fbdae7e6d805 13 - mbed CLI. Run `pip install mbed-cli` to install it.
borlanic 0:fbdae7e6d805 14
borlanic 0:fbdae7e6d805 15 The remainder of this guide assumes:
borlanic 0:fbdae7e6d805 16
borlanic 0:fbdae7e6d805 17 - You are developing on a \*nix machine in the `~/code` folder.
borlanic 0:fbdae7e6d805 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).
borlanic 0:fbdae7e6d805 19
borlanic 0:fbdae7e6d805 20 You can use these instructions as guidelines in the case of other targets on other host OSs.
borlanic 0:fbdae7e6d805 21
borlanic 0:fbdae7e6d805 22 ## Start with the `blinky` app
borlanic 0:fbdae7e6d805 23 [Go to top](#overview)
borlanic 0:fbdae7e6d805 24
borlanic 0:fbdae7e6d805 25 Create a new mbed application called `uvisor-example` by running the following commands:
borlanic 0:fbdae7e6d805 26
borlanic 0:fbdae7e6d805 27 ```bash
borlanic 0:fbdae7e6d805 28 $ cd ~/code
borlanic 0:fbdae7e6d805 29 $ mbed new uvisor-example
borlanic 0:fbdae7e6d805 30 $ cd uvisor-example
borlanic 0:fbdae7e6d805 31 ```
borlanic 0:fbdae7e6d805 32
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 34
borlanic 0:fbdae7e6d805 35 Once the import process finishes, create a `source` folder:
borlanic 0:fbdae7e6d805 36 ```bash
borlanic 0:fbdae7e6d805 37 $ mkdir ~/code/uvisor-example/source
borlanic 0:fbdae7e6d805 38 ```
borlanic 0:fbdae7e6d805 39 Place a new file `main.cpp` in it:
borlanic 0:fbdae7e6d805 40
borlanic 0:fbdae7e6d805 41 ```C
borlanic 0:fbdae7e6d805 42 /* ~/code/uvisor-example/source/main.cpp */
borlanic 0:fbdae7e6d805 43
borlanic 0:fbdae7e6d805 44 #include "mbed.h"
borlanic 0:fbdae7e6d805 45
borlanic 0:fbdae7e6d805 46 DigitalOut led(LED1);
borlanic 0:fbdae7e6d805 47
borlanic 0:fbdae7e6d805 48 int main(void)
borlanic 0:fbdae7e6d805 49 {
borlanic 0:fbdae7e6d805 50 while (true) {
borlanic 0:fbdae7e6d805 51 led = !led;
borlanic 0:fbdae7e6d805 52 wait(0.5);
borlanic 0:fbdae7e6d805 53 }
borlanic 0:fbdae7e6d805 54 }
borlanic 0:fbdae7e6d805 55 ```
borlanic 0:fbdae7e6d805 56
borlanic 0:fbdae7e6d805 57 This application blinks an LED from the main thread, which the OS creates by default.
borlanic 0:fbdae7e6d805 58
borlanic 0:fbdae7e6d805 59 ---
borlanic 0:fbdae7e6d805 60
borlanic 0:fbdae7e6d805 61 **Checkpoint**
borlanic 0:fbdae7e6d805 62
borlanic 0:fbdae7e6d805 63 Compile the application:
borlanic 0:fbdae7e6d805 64
borlanic 0:fbdae7e6d805 65 ```bash
borlanic 0:fbdae7e6d805 66 $ mbed compile -m K64F -t GCC_ARM
borlanic 0:fbdae7e6d805 67 ```
borlanic 0:fbdae7e6d805 68
borlanic 0:fbdae7e6d805 69 The resulting binary is located at:
borlanic 0:fbdae7e6d805 70
borlanic 0:fbdae7e6d805 71 ```bash
borlanic 0:fbdae7e6d805 72 ~/code/uvisor-example/BUILD/K64F/GCC_ARM/uvisor-example.bin
borlanic 0:fbdae7e6d805 73 ```
borlanic 0:fbdae7e6d805 74
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 76
borlanic 0:fbdae7e6d805 77 ## Enable uVisor
borlanic 0:fbdae7e6d805 78 [Go to top](#overview)
borlanic 0:fbdae7e6d805 79
borlanic 0:fbdae7e6d805 80 To enable the uVisor on the app, add these lines to the beginning of the `main.cpp` file:
borlanic 0:fbdae7e6d805 81
borlanic 0:fbdae7e6d805 82 ```C
borlanic 0:fbdae7e6d805 83 /* ~/code/uvisor-example/source/main.cpp */
borlanic 0:fbdae7e6d805 84
borlanic 0:fbdae7e6d805 85 #include "mbed.h"
borlanic 0:fbdae7e6d805 86 #include "uvisor-lib/uvisor-lib.h"
borlanic 0:fbdae7e6d805 87
borlanic 0:fbdae7e6d805 88 /* Public box Access Control Lists (ACLs). */
borlanic 0:fbdae7e6d805 89 /* Note: These are specific to the NXP FRDM-K64F board. See the section below
borlanic 0:fbdae7e6d805 90 * for more information. */
borlanic 0:fbdae7e6d805 91 static const UvisorBoxAclItem g_public_box_acls[] = {
borlanic 0:fbdae7e6d805 92 /* For the LED */
borlanic 0:fbdae7e6d805 93 {SIM, sizeof(*SIM), UVISOR_TACLDEF_PERIPH},
borlanic 0:fbdae7e6d805 94 {PORTB, sizeof(*PORTB), UVISOR_TACLDEF_PERIPH},
borlanic 0:fbdae7e6d805 95
borlanic 0:fbdae7e6d805 96 /* For messages printed on the serial port */
borlanic 0:fbdae7e6d805 97 {OSC, sizeof(*OSC), UVISOR_TACLDEF_PERIPH},
borlanic 0:fbdae7e6d805 98 {MCG, sizeof(*MCG), UVISOR_TACLDEF_PERIPH},
borlanic 0:fbdae7e6d805 99 {UART0, sizeof(*UART0), UVISOR_TACLDEF_PERIPH},
borlanic 0:fbdae7e6d805 100 {PIT, sizeof(*PIT), UVISOR_TACLDEF_PERIPH},
borlanic 0:fbdae7e6d805 101 };
borlanic 0:fbdae7e6d805 102
borlanic 0:fbdae7e6d805 103 /* Enable uVisor, using the ACLs we just created. */
borlanic 0:fbdae7e6d805 104 UVISOR_SET_MODE_ACL(UVISOR_ENABLED, g_public_box_acls);
borlanic 0:fbdae7e6d805 105
borlanic 0:fbdae7e6d805 106 /* Rest of the existing code */
borlanic 0:fbdae7e6d805 107 ...
borlanic 0:fbdae7e6d805 108 ```
borlanic 0:fbdae7e6d805 109
borlanic 0:fbdae7e6d805 110 In the code above, we specified two elements:
borlanic 0:fbdae7e6d805 111
borlanic 0:fbdae7e6d805 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).
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 114
borlanic 0:fbdae7e6d805 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:
borlanic 0:fbdae7e6d805 116
borlanic 0:fbdae7e6d805 117 ```JSON
borlanic 0:fbdae7e6d805 118 {
borlanic 0:fbdae7e6d805 119 "target_overrides": {
borlanic 0:fbdae7e6d805 120 "*": {
borlanic 0:fbdae7e6d805 121 "target.features_add": ["UVISOR"],
borlanic 0:fbdae7e6d805 122 "target.extra_labels_add": ["UVISOR_SUPPORTED"]
borlanic 0:fbdae7e6d805 123 }
borlanic 0:fbdae7e6d805 124 },
borlanic 0:fbdae7e6d805 125 "macros": [
borlanic 0:fbdae7e6d805 126 "FEATURE_UVISOR=1",
borlanic 0:fbdae7e6d805 127 "TARGET_UVISOR_SUPPORTED=1"
borlanic 0:fbdae7e6d805 128 ]
borlanic 0:fbdae7e6d805 129 }
borlanic 0:fbdae7e6d805 130 ```
borlanic 0:fbdae7e6d805 131
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 133
borlanic 0:fbdae7e6d805 134 ---
borlanic 0:fbdae7e6d805 135
borlanic 0:fbdae7e6d805 136 **Checkpoint**
borlanic 0:fbdae7e6d805 137
borlanic 0:fbdae7e6d805 138 Compile the application again. This time, the `K64F` target includes the new features and labels you provided in `mbed_app.json`;
borlanic 0:fbdae7e6d805 139
borlanic 0:fbdae7e6d805 140 ```bash
borlanic 0:fbdae7e6d805 141 $ mbed compile -m K64F -t GCC_ARM
borlanic 0:fbdae7e6d805 142 ```
borlanic 0:fbdae7e6d805 143
borlanic 0:fbdae7e6d805 144 The binary is located at:
borlanic 0:fbdae7e6d805 145
borlanic 0:fbdae7e6d805 146 ```bash
borlanic 0:fbdae7e6d805 147 ~/code/uvisor-example/BUILD/K64F/GCC_ARM/uvisor-example.bin
borlanic 0:fbdae7e6d805 148 ```
borlanic 0:fbdae7e6d805 149
borlanic 0:fbdae7e6d805 150 Reflash the device, and press the reset button. The device LED blinks as in the previous case.
borlanic 0:fbdae7e6d805 151
borlanic 0:fbdae7e6d805 152 ---
borlanic 0:fbdae7e6d805 153
borlanic 0:fbdae7e6d805 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*.
borlanic 0:fbdae7e6d805 155
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 157
borlanic 0:fbdae7e6d805 158 ## Add a secure box
borlanic 0:fbdae7e6d805 159 [Go to top](#overview)
borlanic 0:fbdae7e6d805 160
borlanic 0:fbdae7e6d805 161 Now that uVisor is enabled, you can finally add a *secure box*.
borlanic 0:fbdae7e6d805 162
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 164
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 166
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 168
borlanic 0:fbdae7e6d805 169 ### Configure the secure box
borlanic 0:fbdae7e6d805 170
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 172
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 174
borlanic 0:fbdae7e6d805 175 You want the box to have exclusive access to the following resources:
borlanic 0:fbdae7e6d805 176
borlanic 0:fbdae7e6d805 177 - The push-button peripheral (as specified by a peripheral ACL). Nobody else should be able to access the push-button port.
borlanic 0:fbdae7e6d805 178 - The push-button interrupt (as specified by an IRQ ACL). You want the button IRQ to reroute to our box-specific ISR.
borlanic 0:fbdae7e6d805 179 - The private dynamically allocated buffer (as specified by a dynamic memory ACL).
borlanic 0:fbdae7e6d805 180 - The private variables (as specified by a static memory ACL).
borlanic 0:fbdae7e6d805 181
borlanic 0:fbdae7e6d805 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`.
borlanic 0:fbdae7e6d805 183
borlanic 0:fbdae7e6d805 184 ```C
borlanic 0:fbdae7e6d805 185 /* ~/code/uvisor-example/source/secure_box.cpp */
borlanic 0:fbdae7e6d805 186
borlanic 0:fbdae7e6d805 187 #include "mbed.h"
borlanic 0:fbdae7e6d805 188 #include "uvisor-lib/uvisor-lib.h"
borlanic 0:fbdae7e6d805 189
borlanic 0:fbdae7e6d805 190 /* Private static memory for the secure box */
borlanic 0:fbdae7e6d805 191 typedef struct {
borlanic 0:fbdae7e6d805 192 uint32_t * buffer; /* Static private memory, pointing to dynamically allocated private memory */
borlanic 0:fbdae7e6d805 193 uint32_t counter; /* Static private memory */
borlanic 0:fbdae7e6d805 194 int index; /* Static private memory */
borlanic 0:fbdae7e6d805 195 RawSerial * pc; /* Static private memory, pointing to dynamically allocated private memory */
borlanic 0:fbdae7e6d805 196 } PrivateButtonStaticMemory;
borlanic 0:fbdae7e6d805 197
borlanic 0:fbdae7e6d805 198 /* ACLs list for the secure box: Timer (PIT). */
borlanic 0:fbdae7e6d805 199 static const UvisorBoxAclItem g_private_button_acls[] = {
borlanic 0:fbdae7e6d805 200 {PORTC, sizeof(*PORTC), UVISOR_TACLDEF_PERIPH}, /* Private peripheral */
borlanic 0:fbdae7e6d805 201 {(void *) PORTC_IRQn, 0, UVISOR_TACL_IRQ}, /* Private IRQ */
borlanic 0:fbdae7e6d805 202 };
borlanic 0:fbdae7e6d805 203
borlanic 0:fbdae7e6d805 204 static void private_button_main_thread(const void *);
borlanic 0:fbdae7e6d805 205
borlanic 0:fbdae7e6d805 206 /* Secure box configuration */
borlanic 0:fbdae7e6d805 207 UVISOR_BOX_NAMESPACE(NULL); /* We won't specify a box namespace for this example. */
borlanic 0:fbdae7e6d805 208 UVISOR_BOX_HEAPSIZE(4096); /* Heap size for the secure box */
borlanic 0:fbdae7e6d805 209 UVISOR_BOX_MAIN(private_button_main_thread, /* Main thread for the secure box */
borlanic 0:fbdae7e6d805 210 osPriorityNormal, /* Priority of the secure box's main thread */
borlanic 0:fbdae7e6d805 211 1024); /* Stack size for the secure box's main thread */
borlanic 0:fbdae7e6d805 212 UVISOR_BOX_CONFIG(private_button, /* Name of the secure box */
borlanic 0:fbdae7e6d805 213 g_private_button_acls, /* ACLs list for the secure box */
borlanic 0:fbdae7e6d805 214 1024, /* Stack size for the secure box */
borlanic 0:fbdae7e6d805 215 PrivateButtonStaticMemory); /* Private static memory for the secure box. */
borlanic 0:fbdae7e6d805 216 ```
borlanic 0:fbdae7e6d805 217
borlanic 0:fbdae7e6d805 218 ### Create the secure box's main thread function
borlanic 0:fbdae7e6d805 219
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 221
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 223
borlanic 0:fbdae7e6d805 224 ```C
borlanic 0:fbdae7e6d805 225 /* ~/code/uvisor-example/source/secure_box.cpp */
borlanic 0:fbdae7e6d805 226
borlanic 0:fbdae7e6d805 227 /* The previous code goes here. */
borlanic 0:fbdae7e6d805 228 ...
borlanic 0:fbdae7e6d805 229
borlanic 0:fbdae7e6d805 230 #define uvisor_ctx ((PrivateButtonStaticMemory *) __uvisor_ctx)
borlanic 0:fbdae7e6d805 231
borlanic 0:fbdae7e6d805 232 #define PRIVATE_BUTTON_BUFFER_COUNT 8
borlanic 0:fbdae7e6d805 233
borlanic 0:fbdae7e6d805 234 static void private_button_on_press(void)
borlanic 0:fbdae7e6d805 235 {
borlanic 0:fbdae7e6d805 236 /* Store the thread count into the buffer and reset it. */
borlanic 0:fbdae7e6d805 237 uvisor_ctx->buffer[uvisor_ctx->index] = uvisor_ctx->counter;
borlanic 0:fbdae7e6d805 238 uvisor_ctx->counter = 0;
borlanic 0:fbdae7e6d805 239
borlanic 0:fbdae7e6d805 240 /* Update the index. Behave as a circular buffer. */
borlanic 0:fbdae7e6d805 241 if (uvisor_ctx->index < PRIVATE_BUTTON_BUFFER_COUNT - 1) {
borlanic 0:fbdae7e6d805 242 uvisor_ctx->index++;
borlanic 0:fbdae7e6d805 243 } else {
borlanic 0:fbdae7e6d805 244 uvisor_ctx->index = 0;
borlanic 0:fbdae7e6d805 245
borlanic 0:fbdae7e6d805 246 /* For debug purposes: Print the content of the buffer. */
borlanic 0:fbdae7e6d805 247 uvisor_ctx->pc->printf("Thread count between button presses: ");
borlanic 0:fbdae7e6d805 248 for (int i = 0; i < PRIVATE_BUTTON_BUFFER_COUNT; ++i) {
borlanic 0:fbdae7e6d805 249 uvisor_ctx->pc->printf("%lu ", uvisor_ctx->buffer[i]);
borlanic 0:fbdae7e6d805 250 }
borlanic 0:fbdae7e6d805 251 uvisor_ctx->pc->printf("\n");
borlanic 0:fbdae7e6d805 252 }
borlanic 0:fbdae7e6d805 253
borlanic 0:fbdae7e6d805 254 }
borlanic 0:fbdae7e6d805 255
borlanic 0:fbdae7e6d805 256 /* Main thread for the secure box */
borlanic 0:fbdae7e6d805 257 static void private_button_main_thread(const void *)
borlanic 0:fbdae7e6d805 258 {
borlanic 0:fbdae7e6d805 259 /* Allocate serial port to ensure that code in this secure box
borlanic 0:fbdae7e6d805 260 * won't touch handle in the default security context when printing */
borlanic 0:fbdae7e6d805 261 if (!(uvisor_ctx->pc = new RawSerial(USBTX, USBRX))) {
borlanic 0:fbdae7e6d805 262 return;
borlanic 0:fbdae7e6d805 263 }
borlanic 0:fbdae7e6d805 264
borlanic 0:fbdae7e6d805 265 /* Create the buffer and cache its pointer to the private static memory. */
borlanic 0:fbdae7e6d805 266 uvisor_ctx->buffer = (uint32_t *) malloc(PRIVATE_BUTTON_BUFFER_COUNT * sizeof(uint32_t));
borlanic 0:fbdae7e6d805 267 if (uvisor_ctx->buffer == NULL) {
borlanic 0:fbdae7e6d805 268 uvisor_ctx->pc->printf("ERROR: Failed to allocate memory for the button buffer\n");
borlanic 0:fbdae7e6d805 269 mbed_die();
borlanic 0:fbdae7e6d805 270 }
borlanic 0:fbdae7e6d805 271 uvisor_ctx->index = 0;
borlanic 0:fbdae7e6d805 272 uvisor_ctx->counter = 0;
borlanic 0:fbdae7e6d805 273
borlanic 0:fbdae7e6d805 274 /* Setup the push-button callback. */
borlanic 0:fbdae7e6d805 275 InterruptIn button(SW2); /* Private IRQ */
borlanic 0:fbdae7e6d805 276 button.mode(PullUp);
borlanic 0:fbdae7e6d805 277 button.fall(&private_button_on_press);
borlanic 0:fbdae7e6d805 278
borlanic 0:fbdae7e6d805 279 /* Increment the private counter every second. */
borlanic 0:fbdae7e6d805 280 while (1) {
borlanic 0:fbdae7e6d805 281 uvisor_ctx->counter++;
borlanic 0:fbdae7e6d805 282 wait(1.0);
borlanic 0:fbdae7e6d805 283 }
borlanic 0:fbdae7e6d805 284 }
borlanic 0:fbdae7e6d805 285 ```
borlanic 0:fbdae7e6d805 286
borlanic 0:fbdae7e6d805 287 A few things to note in the code above:
borlanic 0:fbdae7e6d805 288
borlanic 0:fbdae7e6d805 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`.
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 293
borlanic 0:fbdae7e6d805 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`).
borlanic 0:fbdae7e6d805 295
borlanic 0:fbdae7e6d805 296 ---
borlanic 0:fbdae7e6d805 297
borlanic 0:fbdae7e6d805 298 **Checkpoint**
borlanic 0:fbdae7e6d805 299
borlanic 0:fbdae7e6d805 300 Compile the application again:
borlanic 0:fbdae7e6d805 301
borlanic 0:fbdae7e6d805 302 ```bash
borlanic 0:fbdae7e6d805 303 $ mbed compile -m K64F -t GCC_ARM
borlanic 0:fbdae7e6d805 304 ```
borlanic 0:fbdae7e6d805 305
borlanic 0:fbdae7e6d805 306 Reflash the device, and press the reset button. The device LED blinks.
borlanic 0:fbdae7e6d805 307
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 309
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 311
borlanic 0:fbdae7e6d805 312 ## Expose public secure entry points to the secure box
borlanic 0:fbdae7e6d805 313 [Go to top](#overview)
borlanic 0:fbdae7e6d805 314
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 316
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 318
borlanic 0:fbdae7e6d805 319 ### Defining a secure entry point
borlanic 0:fbdae7e6d805 320
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 322
borlanic 0:fbdae7e6d805 323 ```cpp
borlanic 0:fbdae7e6d805 324 /* ~/code/uvisor-example/source/secure_box.h */
borlanic 0:fbdae7e6d805 325
borlanic 0:fbdae7e6d805 326 #ifndef SECURE_BOX_H_
borlanic 0:fbdae7e6d805 327 #define SECURE_BOX_H_
borlanic 0:fbdae7e6d805 328
borlanic 0:fbdae7e6d805 329 #include "uvisor-lib/uvisor-lib.h"
borlanic 0:fbdae7e6d805 330
borlanic 0:fbdae7e6d805 331 UVISOR_EXTERN int (*secure_get_index)(void);
borlanic 0:fbdae7e6d805 332
borlanic 0:fbdae7e6d805 333 #endif
borlanic 0:fbdae7e6d805 334 ```
borlanic 0:fbdae7e6d805 335
borlanic 0:fbdae7e6d805 336 ### Implementing a secure entry point
borlanic 0:fbdae7e6d805 337
borlanic 0:fbdae7e6d805 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:
borlanic 0:fbdae7e6d805 339
borlanic 0:fbdae7e6d805 340 ```cpp
borlanic 0:fbdae7e6d805 341 /* ~/code/uvisor-example/source/secure_box.cpp */
borlanic 0:fbdae7e6d805 342
borlanic 0:fbdae7e6d805 343 /* Function called through RPC */
borlanic 0:fbdae7e6d805 344 static int get_index() {
borlanic 0:fbdae7e6d805 345 /* Access to private memory here */
borlanic 0:fbdae7e6d805 346 return uvisor_ctx->index;
borlanic 0:fbdae7e6d805 347 }
borlanic 0:fbdae7e6d805 348
borlanic 0:fbdae7e6d805 349 UVISOR_BOX_RPC_GATEWAY_SYNC (private_button, secure_get_index, get_index, int, void);
borlanic 0:fbdae7e6d805 350
borlanic 0:fbdae7e6d805 351 #define PRIVATE_BUTTON_BUFFER_COUNT 8
borlanic 0:fbdae7e6d805 352 ```
borlanic 0:fbdae7e6d805 353
borlanic 0:fbdae7e6d805 354 ### Listening for RPC messages
borlanic 0:fbdae7e6d805 355
borlanic 0:fbdae7e6d805 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:
borlanic 0:fbdae7e6d805 357
borlanic 0:fbdae7e6d805 358 ```cpp
borlanic 0:fbdae7e6d805 359 /* ~/code/uvisor-example/source/secure_box.cpp */
borlanic 0:fbdae7e6d805 360
borlanic 0:fbdae7e6d805 361 static void listen_for_rpc() {
borlanic 0:fbdae7e6d805 362 /* List of functions to wait for */
borlanic 0:fbdae7e6d805 363 static const TFN_Ptr my_fn_array[] = {
borlanic 0:fbdae7e6d805 364 (TFN_Ptr) get_index
borlanic 0:fbdae7e6d805 365 };
borlanic 0:fbdae7e6d805 366
borlanic 0:fbdae7e6d805 367 while (1) {
borlanic 0:fbdae7e6d805 368 int caller_id;
borlanic 0:fbdae7e6d805 369 int status = rpc_fncall_waitfor(my_fn_array, 1, &caller_id, UVISOR_WAIT_FOREVER);
borlanic 0:fbdae7e6d805 370
borlanic 0:fbdae7e6d805 371 if (status) {
borlanic 0:fbdae7e6d805 372 uvisor_error(USER_NOT_ALLOWED);
borlanic 0:fbdae7e6d805 373 }
borlanic 0:fbdae7e6d805 374 }
borlanic 0:fbdae7e6d805 375 }
borlanic 0:fbdae7e6d805 376
borlanic 0:fbdae7e6d805 377 /* Main thread for the secure box */
borlanic 0:fbdae7e6d805 378 static void private_button_main_thread(const void *)
borlanic 0:fbdae7e6d805 379 {
borlanic 0:fbdae7e6d805 380 /* allocate serial port to ensure that code in this secure box
borlanic 0:fbdae7e6d805 381 * won't touch handle in the default security context when printing */
borlanic 0:fbdae7e6d805 382 if (!(uvisor_ctx->pc = new RawSerial(USBTX, USBRX)))
borlanic 0:fbdae7e6d805 383 return;
borlanic 0:fbdae7e6d805 384
borlanic 0:fbdae7e6d805 385 /* Start listening for RPC messages in a separate thread */
borlanic 0:fbdae7e6d805 386 Thread rpc_thread(osPriorityNormal, 1024);
borlanic 0:fbdae7e6d805 387 rpc_thread.start(&listen_for_rpc);
borlanic 0:fbdae7e6d805 388
borlanic 0:fbdae7e6d805 389 /* ... Rest of the private_button_main_thread function ... */
borlanic 0:fbdae7e6d805 390 ```
borlanic 0:fbdae7e6d805 391
borlanic 0:fbdae7e6d805 392 ### Calling the public secure entry point
borlanic 0:fbdae7e6d805 393
borlanic 0:fbdae7e6d805 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:
borlanic 0:fbdae7e6d805 395
borlanic 0:fbdae7e6d805 396 ```cpp
borlanic 0:fbdae7e6d805 397 /* ~/code/uvisor-example/source/main.cpp */
borlanic 0:fbdae7e6d805 398
borlanic 0:fbdae7e6d805 399 #include "secure_box.h"
borlanic 0:fbdae7e6d805 400 ```
borlanic 0:fbdae7e6d805 401
borlanic 0:fbdae7e6d805 402 Then replace the `main` function with:
borlanic 0:fbdae7e6d805 403
borlanic 0:fbdae7e6d805 404 ```cpp
borlanic 0:fbdae7e6d805 405 /* ~/code/uvisor-example/source/main.cpp */
borlanic 0:fbdae7e6d805 406
borlanic 0:fbdae7e6d805 407 int main(void)
borlanic 0:fbdae7e6d805 408 {
borlanic 0:fbdae7e6d805 409 while (true) {
borlanic 0:fbdae7e6d805 410 led = !led;
borlanic 0:fbdae7e6d805 411 printf("Secure index is %d\n", secure_get_index());
borlanic 0:fbdae7e6d805 412 Thread::wait(500);
borlanic 0:fbdae7e6d805 413 }
borlanic 0:fbdae7e6d805 414 }
borlanic 0:fbdae7e6d805 415 ```
borlanic 0:fbdae7e6d805 416
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 418
borlanic 0:fbdae7e6d805 419 ## The NVIC APIs
borlanic 0:fbdae7e6d805 420
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 422
borlanic 0:fbdae7e6d805 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:
borlanic 0:fbdae7e6d805 424
borlanic 0:fbdae7e6d805 425 - The uVisor owns the interrupt vector table.
borlanic 0:fbdae7e6d805 426 - All ISRs are relocated to SRAM.
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 428 - An IRQ that belongs to a box can only be modified when that box context is active.
borlanic 0:fbdae7e6d805 429
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 431
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 433
borlanic 0:fbdae7e6d805 434 ## The *public box* ACLs
borlanic 0:fbdae7e6d805 435
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 437
borlanic 0:fbdae7e6d805 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:
borlanic 0:fbdae7e6d805 439
borlanic 0:fbdae7e6d805 440 ```C
borlanic 0:fbdae7e6d805 441 static const UvisorBoxAclItem g_public_box_acls[] = {
borlanic 0:fbdae7e6d805 442 }
borlanic 0:fbdae7e6d805 443 ```
borlanic 0:fbdae7e6d805 444
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 446
borlanic 0:fbdae7e6d805 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:
borlanic 0:fbdae7e6d805 448
borlanic 0:fbdae7e6d805 449 ```
borlanic 0:fbdae7e6d805 450 ***********************************************************
borlanic 0:fbdae7e6d805 451 BUS FAULT
borlanic 0:fbdae7e6d805 452 ***********************************************************
borlanic 0:fbdae7e6d805 453
borlanic 0:fbdae7e6d805 454 * Active Box ID: 0
borlanic 0:fbdae7e6d805 455 * FAULT SYNDROME REGISTERS
borlanic 0:fbdae7e6d805 456
borlanic 0:fbdae7e6d805 457 CFSR: 0x00008200
borlanic 0:fbdae7e6d805 458 BFAR: 0x40048044
borlanic 0:fbdae7e6d805 459 --> PRECISERR: precise data access.
borlanic 0:fbdae7e6d805 460
borlanic 0:fbdae7e6d805 461 ...
borlanic 0:fbdae7e6d805 462 ```
borlanic 0:fbdae7e6d805 463
borlanic 0:fbdae7e6d805 464 Look up the faulty address (the value of BFAR) in the target device reference manual.
borlanic 0:fbdae7e6d805 465
borlanic 0:fbdae7e6d805 466 Once you know which peripheral is causing the fault (the `SIM` peripheral, in this example), add its entry to the ACLs list:
borlanic 0:fbdae7e6d805 467
borlanic 0:fbdae7e6d805 468 ```C
borlanic 0:fbdae7e6d805 469 static const UvisorBoxAclItem g_public_box_acls[] = {
borlanic 0:fbdae7e6d805 470 {SIM, sizeof(*SIM), UVISOR_TACLDEF_PERIPH},
borlanic 0:fbdae7e6d805 471 };
borlanic 0:fbdae7e6d805 472 ```
borlanic 0:fbdae7e6d805 473
borlanic 0:fbdae7e6d805 474 > **Note**: If the fault debug screen does not show the name of the peripheral, look it up in the target device reference manual.
borlanic 0:fbdae7e6d805 475
borlanic 0:fbdae7e6d805 476 For readability, do not use the hard-coded addresses of your peripherals. Instead, use the symbols that the target CMSIS module provides.
borlanic 0:fbdae7e6d805 477
borlanic 0:fbdae7e6d805 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.
borlanic 0:fbdae7e6d805 479
borlanic 0:fbdae7e6d805 480 ## Additional resources
borlanic 0:fbdae7e6d805 481 [Go to top](#overview)
borlanic 0:fbdae7e6d805 482
borlanic 0:fbdae7e6d805 483 - [uVisor API documentation](API.md).
borlanic 0:fbdae7e6d805 484 - [Debugging uVisor on mbed OS](DEBUGGING.md).
borlanic 0:fbdae7e6d805 485 - [Using nonvolatile storage from uVisor on mbed OS](manual/Flash.md).
borlanic 0:fbdae7e6d805 486
borlanic 0:fbdae7e6d805 487 If you found any bug or inconsistency in this guide, please [raise an issue](https://github.com/ARMmbed/uvisor/issues/new).