From 4daae23a07aefee524259a028f5314b985141307 Mon Sep 17 00:00:00 2001 From: AnotherButler Date: Tue, 14 Feb 2017 15:58:28 -0600 Subject: [PATCH] Copy edit uVisor documents Update uVisor documents for active voice, consistency, grammar and spelling --- CONTRIBUTING.md | 27 +- README.md | 140 +++++----- docs/README.md | 16 +- docs/api/API.md | 53 ++-- docs/api/DEBUGGING.md | 103 ++++--- docs/api/QUICKSTART.md | 189 ++++++------- docs/api/manual/UseCases.md | 462 +++++++++++++++----------------- docs/core/DEVELOPING_LOCALLY.md | 58 ++-- docs/core/PORTING.md | 196 ++++++-------- 9 files changed, 552 insertions(+), 692 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 62a48ad6..7547968a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,29 +1,14 @@ -ARM mbed OS is an Open Source project. We encourage you to pitch in, and we -welcome contributions and [bug reports](../../issues). +ARM mbed OS is an open source project. We welcome contributions and [bug reports](../../issues). -* If you want to submit a bug report please make sure to follow our - [reporting guidelines](https://github.com/ARMmbed/mbed-os/blob/master/Reporting%20Bugs.md). +- If you want to submit a bug report, please follow our [reporting guidelines](https://github.com/ARMmbed/mbed-os/blob/master/Reporting%20Bugs.md). -* If you want to submit a patch, please read the - [contribution guide](https://github.com/ARMmbed/mbed-os/blob/master/Contributing%20to%20mbed.md). - Note that we have a - [Contributor Agreement](http://developer.mbed.org/contributor_agreement/) - that must be agreed to before contributions can be merged. To agree to the - contributor agreement, you need to have an mbed.com account and be logged in. - **We only accept [bug reports](../../issues) and pull requests [via GitHub](../../)**. +- If you want to submit a patch, please read the [contribution guide](https://docs.mbed.com/docs/mbed-os-handbook/en/latest/cont/contributing/). Note that we have a [Contributor Agreement](http://developer.mbed.org/contributor_agreement/) that you must agree to before we can merge your contributions. To agree to the contributor agreement, you need to have a [developer.mbed.org](https://developer.mbed.org/account/signup/) account and be logged in. **We only accept [bug reports](../../issues) and pull requests [via GitHub](../../)**. -* If you have a question about how to use mbed OS, please search the - [mbed forums](http://forums.mbed.com/c/mbed-os), and if you still need help, - post a new topic there. +- If you have a question about how to use mbed OS, please search the [mbed forums](http://forums.mbed.com/c/mbed-os), and if you still need help, post a new topic there. -* Before contributing an enhancement (new feature, new port etc) please start by - [discussing it on the forums](http://forums.mbed.com/c/mbed-os) - to avoid duplication of work - as we or others might work on a related feature already. - This will help streamline your pull request for getting it merged quickly. +- Before contributing an enhancement (such as a new feature or port), please start by [discussing it on the forums](http://forums.mbed.com/c/mbed-os) to avoid duplication of work. This will help streamline your pull request for a quick merge. -* If you work for an [mbed Partner company](http://www.mbed.com/en/partners/our-partners/), - you will have a partner manager assigned to you who can help you navigate the - process and get the best out of your partnership. +- If you work for an [mbed Partner company](http://www.mbed.com/en/partners/our-partners/), the partner manager assigned to you can help you navigate the process and get the most out of your partnership. Thanks! :heart: diff --git a/README.md b/README.md index 605a5765..5c9301dd 100644 --- a/README.md +++ b/README.md @@ -1,107 +1,99 @@ # The uVisor -The uVisor is a self-contained software hypervisor that creates independent secure domains on ARM Cortex-M3 and M4 micro-controllers. Its function is to increase resilience against malware and to protect secrets from leaking even among different modules of the same application. You can find a [high level overview here](http://www.slideshare.net/FoolsDelight/practical-realtime-operating-system-security-for-the-masses) ([Download PDF](https://github.com/ARMmbed/uvisor/raw/docs/uVisorSecurity-TechCon2016.pdf)). +The uVisor is a self-contained software hypervisor that creates independent secure domains on ARM Cortex®-M3 and Cortex®-M4 microcontrollers. It increases resilience against malware and protects secrets from leaking even among different modules of the same application. You can find a [high-level overview here](http://www.slideshare.net/FoolsDelight/practical-realtime-operating-system-security-for-the-masses) ([Download PDF](https://github.com/ARMmbed/uvisor/raw/docs/uVisorSecurity-TechCon2016.pdf)). -To start using uVisor you will need to include it as a library in your design. We release the uVisor library periodically into the mbed OS repository, [ARMmbed/mbed-os](https://github.com/ARMmbed/mbed-os). If you want to learn more about the uVisor security model and get an overview of its features this is the right place. In this document you can read about: +To start using uVisor, you need to include it as a library in your design. We release the uVisor library periodically into the mbed OS repository, [ARMmbed/mbed-os](https://github.com/ARMmbed/mbed-os). Review it to learn more about the uVisor security model and see an overview of its features. -* The uVisor design philosophy. -* A technical overview of the uVisor features: - * Memory layout. - * Secure boot. - * Context switching. +You can find most of the uVisor documentation in the [docs](docs) folder. Please look at the [getting started guide](docs/api/QUICKSTART.md) for an introduction to uVisor application development. If you are interested in uVisor internals, please refer to the [OS-level introduction](https://github.com/ARMmbed/uvisor/raw/docs/uvisor-rtos-docs.pdf) and the [uVisor API docs](docs/api/API.md). -You can find most of the uVisor documentation in the [docs](docs) folder. Please have a look at our [quickstart guide](docs/api/QUICKSTART.md) for an introduction into uVisor application devlopment. If you are interested in uVisor internals please refer to the [OS-level introduction](https://github.com/ARMmbed/uvisor/raw/docs/uvisor-rtos-docs.pdf) and our [uVisor API docs](docs/api/API.md). +Contributions to this repository in the form of issue reporting and pull requests are welcome! Please read our [contribution guidelines](CONTRIBUTING.md) first. -Contributions to this repository in the form of issue reporting or pull requests are welcome! Please make sure to read our [contribution guidelines](CONTRIBUTING.md) first. +### Getting started examples -### Getting Started Examples - -* The [basic uVisor example](https://github.com/ARMmbed/mbed-os-example-uvisor) shows how secure interrupts and C++ objects are instantiated in the context of secure boxes. -* In our [uVisor threaded example](https://github.com/ARMmbed/mbed-os-example-uvisor-thread) we demonstrate the configuration of multiple boxes containing secure threads. -* For secure communication between boxes and implementation of secure APIs we introduced the [secure number store example](https://github.com/ARMmbed/mbed-os-example-uvisor-number-store/). The example shows how a called box can infer the callers identity and ownership of secure objects across boots and firmware updates. +- The [basic uVisor example](https://github.com/ARMmbed/mbed-os-example-uvisor) shows how secure interrupts and C++ objects are instantiated in the context of secure boxes. +- The [uVisor threaded example](https://github.com/ARMmbed/mbed-os-example-uvisor-thread) demonstrates the configuration of multiple boxes containing secure threads. +- The [secure number store example](https://github.com/ARMmbed/mbed-os-example-uvisor-number-store/) demonstrates secure communication between boxes and implementation of secure APIs. The example shows how a called box can infer the caller's identity and ownership of secure objects across boots and firmware updates. ### Word of caution This version of the uVisor is an early technology preview with an **incomplete implementation of the security features** of the final product. Future versions of uVisor will add these functions. -Some of the open uVisor issues in progress are listed here: +You can find some of the open uVisor issues here: -* [Known issues](https://github.com/ARMmbed/uvisor/issues?q=is%3Aopen+is%3Aissue+label%3Aissue) -* [FIXMEs](https://github.com/ARMmbed/uvisor/search?utf8=%E2%9C%93&q=FIXME) +- [Known issues](https://github.com/ARMmbed/uvisor/issues?q=is%3Aopen+is%3Aissue+label%3Aissue). +- [FIXMEs](https://github.com/ARMmbed/uvisor/search?utf8=%E2%9C%93&q=FIXME). -### Further reading: +### Further reading -* [Practical real-time operating system security for the masses](https://github.com/ARMmbed/uvisor/raw/docs/uVisorSecurity-TechCon2016.pdf): ARM TechCon 2016 PDF presentation of the uVisor design philosophy and technical overview. -* [mbed uVisor integration in mbed OS](https://github.com/ARMmbed/uvisor/raw/docs/uvisor-rtos-docs.pdf) in PDF format. -* [Q&A with ARM](http://eecatalog.com/IoT/2015/08/18/qa-with-arm-securing-the-iot-using-arm-cortex-processors-and-a-growing-mbed-platform-suite/): Securing the IoT using ARM Cortex processors, and a growing mbed platform suite. +- [Practical real-time operating system security for the masses](https://github.com/ARMmbed/uvisor/raw/docs/uVisorSecurity-TechCon2016.pdf): ARM TechCon 2016 PDF presentation of the uVisor design philosophy and technical overview. +- [mbed uVisor integration in mbed OS](https://github.com/ARMmbed/uvisor/raw/docs/uvisor-rtos-docs.pdf) in PDF format. +- [Q&A with ARM](http://eecatalog.com/IoT/2015/08/18/qa-with-arm-securing-the-iot-using-arm-cortex-processors-and-a-growing-mbed-platform-suite/): Securing the IoT using ARM Cortex® processors and a growing mbed platform suite. ### Supported platforms -The following platforms are currently supported by the uVisor core: +The uVisor core supports the following platforms: -* [NXP FRDM-K64F](http://developer.mbed.org/platforms/FRDM-K64F/) -* [STMicorelectronics STM32F429I-DISCO](http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/PF259090) -* [Silicon Labs EFM32 Gecko](http://www.silabs.com/products/mcu/32-bit/efm32-gecko/pages/efm32-gecko.aspx) (Cortex M3 and M4 devices). +- [NXP FRDM-K64F](http://developer.mbed.org/platforms/FRDM-K64F/). +- [STMicroelectronics STM32F429I-DISCO](http://www.st.com/web/catalog/tools/FM116/SC959/SS1532/PF259090). +- [Silicon Labs EFM32 Gecko](http://www.silabs.com/products/mcu/32-bit/efm32-gecko/pages/efm32-gecko.aspx) (Cortex®-M3 and Cortex®-M4 devices). -To use uVisor on a specific OS, though, the porting process needs to be completed for that OS as well. This requires an additional porting step, which is documented in the [uVisor Porting Guide for mbed OS](docs/core/PORTING.md). The following operating systems are currently supported: +To use uVisor on a specific OS, you must complete the porting process for that OS. This requires an additional porting step, which the [uVisor porting guide for mbed OS](docs/core/PORTING.md) documents. uVisor supports the following operating system: -* mbed OS: [NXP FRDM-K64F](http://developer.mbed.org/platforms/FRDM-K64F/). +- mbed OS: [NXP FRDM-K64F](http://developer.mbed.org/platforms/FRDM-K64F/). -The uVisor pre-linked binary images are built with the Launchpad [GCC ARM Embedded](https://launchpad.net/gcc-arm-embedded) toolchain. Currently only applications built with the same toolchain are supported. +The Launchpad [GNU ARM Embedded](https://launchpad.net/gcc-arm-embedded) Toolchain builds the uVisor prelinked binary images. Currently, uVisor only supports applications built with this toolchain. ## The uVisor design philosophy -The need for security features applies across a wide range of today’s IoT products. We at ARM are convinced that many IoT security problems can be solved with standardised building blocks. - -The uVisor is one of these basic building blocks – complementary to other important blocks like robust communication stacks, safe firmware updates and secure crypto libraries. +The need for security features applies across a wide range of today’s IoT products. Many IoT security problems can be solved with standardized building blocks. The uVisor is one of these basic building blocks – complementary to other important blocks such as robust communication stacks, safe firmware updates and secure crypto libraries. -The design philosophy of uVisor is to provide hardware-enforced compartments (sandboxes) for individual code blocks by limiting access to memories and peripherals using the existing hardware security features of the Cortex-M micro-controllers. +uVisor provides hardware-enforced compartments (sandboxes) for individual code blocks by limiting access to memories and peripherals using the existing hardware security features of the Cortex®-M microcontrollers. -Breaking the established flat security model of micro-controllers into compartmentalised building blocks results in high security levels, as the reach of flaws or external attacks can be limited to less sensitive function blocks. +Breaking the established flat security model of microcontrollers into compartmentalized building blocks results in high security levels because the reach of flaws and external attacks can be limited to less sensitive function blocks. -Our [uVisor example applications](#getting-started-examples) we demonstrate features to prevent unauthorised access to flash memory from faulty or compromised code and interrupts. This not only prevents malware from getting resident on the device, but also enables protection of device secrets like cryptographic keys. +The [uVisor example applications](#getting-started-examples) demonstrate features to prevent unauthorized access to flash memory from faulty or compromised code and interrupts. This prevents malware from getting resident on the device and enables protection of device secrets such as cryptographic keys. -Services built on top of our security layer can safely depend on an unclonable trusted identity, secure access to internet services and benefit from encryption key protection. +Services built on top of the security layer can safely depend on an unclonable trusted identity, secure access to internet services and benefit from encryption key protection. ## Technical overview The uVisor: -* Is initialised right after device start-up. -* Runs in privileged mode. -* Sets up a protected environment using a Memory Protection Unit (the ARM Cortex-M MPU or a vendor-specific alternative). In particular: - * Its own memories and the security-critical peripherals are protected from the unprivileged code. - * Unprivileged access to selected hardware peripherals and memories is limited through Access Control Lists (ACLs). -* Allows interaction from the unprivileged code by exposing SVCall-based APIs. -* Forwards and de-privileges interrupts to the unprivileged handler that has been registered for them. -* Prevents registers leakage when switching execution between privileged and unprivileged code and between mutually untrusted unprivileged modules. -* Forces access to some security-critical peripherals (like DMA) through SVCall-based APIs. +- Is initialized right after device startup. +- Runs in privileged mode. +- Sets up a protected environment using a Memory Protection Unit (MPU), such as the ARM Cortex®-M MPU or a vendor-specific alternative. In particular: + - Its own memories and the security-critical peripherals are protected from the unprivileged code. + - Access Control Lists (ACLs) limit unprivileged access to selected hardware peripherals and memories. +- Allows interaction from the unprivileged code by exposing SVCall-based APIs. +- Forwards and deprivileges interrupts to the unprivileged handler that has been registered for them. +- Prevents registers leakage when switching execution between privileged and unprivileged code and between mutually untrusted unprivileged modules. +- Forces access to some security-critical peripherals (such as DMA) through SVCall-based APIs. ## The unprivileged code All the code that is not explicitly part of the uVisor is generally referred to as unprivileged code. The unprivileged code: -* Runs in unprivileged mode. -* Has direct memory access to unrestricted unprivileged peripherals. -* Can require exclusive access to memories and peripherals. -* Can register for unprivileged interrupts. -* Cannot access privileged memories and peripherals. +- Runs in unprivileged mode. +- Has direct memory access to unrestricted unprivileged peripherals. +- Can require exclusive access to memories and peripherals. +- Can register for unprivileged interrupts. +- Cannot access privileged memories and peripherals. The unprivileged code can be made of mutually untrusted isolated modules (or boxes). This way, even if all are running with unprivileged permissions, different modules can protect their own secrets and execute critical code securely. -For more details on how to setup a secure box and protect memories and peripherals, please read the [Quick-Start Guide for uVisor on mbed OS](docs/api/QUICKSTART.md). +For more details about how to setup a secure box and protect memories and peripherals, please read the [getting started guide](docs/api/QUICKSTART.md). ### Memory layout -Different memory layouts can be used on different platforms, depending on the implemented memory protection scheme and on the MPU architecture. The following figure shows the memory layout of a system where the uVisor shares the SRAM module with the operating system (ARMv7-M MPU). +Different memory layouts can be used on different platforms, depending on the implemented memory protection scheme and the MPU architecture. The following figure shows the memory layout of a system where the uVisor shares the SRAM module with the operating system (ARMv7-M MPU). -![uVisor memory layout](docs/img/memory_layout.png) +[uVisor memory layout](docs/img/memory_layout.png) -The uVisor secures two main memory blocks, in flash and SRAM respectively. In both cases, it protects its own data and the data of the secure boxes it manages for the unprivileged code. For a more detailed view please refer to our interactive [linker section visualization](https://meriac.github.io/mbed-os-linker-report/)). +The uVisor secures two main memory blocks, in flash and SRAM respectively. In both cases, it protects its own data and the data of the secure boxes it manages for the unprivileged code. For a more detailed view, please refer to the interactive [linker section visualization](https://meriac.github.io/mbed-os-linker-report/). All the unprivileged code that is not protected in a secure domain is referred to as the *public box*. -The main memory sections that the uVisor protects are detailed in the following table: +This table details the main memory sections that the uVisor protects: @@ -111,12 +103,12 @@ The main memory sections that the uVisor protects are detailed in the following - - @@ -132,35 +124,35 @@ The main memory sections that the uVisor protects are detailed in the following
uVisor codeThe uVisor code is readable and executable by unprivileged code, so that code sharing is facilitated and privileged-unprivileged transitions are easier. + Unprivileged code can read and execute the uVisor code, so code sharing is facilitated, and privileged-unprivileged transitions are easier.
uVisor data / BSS / stackThe uVisor places all its constants, initialised and uninitialised data and the stack in secured areas of memory, separated from the unprivileged code. + The uVisor places all its constants, initialized and uninitialized data and the stack in secured areas of memory, separated from the unprivileged code.
-If you want to know how to use the uVisor APIs to setup a secure box please refer to the [Quick-Start Guide for uVisor on mbed OS](docs/api/QUICKSTART.md) and to the full [uVisor API documentation](docs/api/API.md). +To use the uVisor APIs to set up a secure box, please refer to the [getting started guide](docs/api/QUICKSTART.md) and the full [uVisor API documentation](docs/api/API.md). ### The boot process -The uVisor is initialised right after device start-up and takes ownership of its most critical assets, like privileged peripherals, the vector table and memory management. The boot process involves the following steps, in this order: +The uVisor is initialized right after device startup and takes ownership of its most critical assets, such as privileged peripherals, the vector table and memory management. The boot process involves the following steps, in this order: -1. Several sanity checks are performed, to verify integrity of the memory structure as expected by the uVisor. -1. The uVisor `bss` section is zeroed, the uVisor `data` section initialised. -1. The uVisor takes ownership of the vector table +1. Several sanity checks verify integrity of the memory structure as expected by the uVisor. +1. The uVisor `bss` section is zeroed, the uVisor `data` section initialized. +1. The uVisor takes ownership of the vector table. 1. The virtual Memory Protection Unit (vMPU) is initialized: - * Secure boxes are loaded. For each of them: - * The `bss` section is zeroed. - * Access Control Lists (ACLs) are registered. - * Stacks are initialised. - * A private box context is initialized, if required. - * The MPU (ARM or third-party) is configured. -1. Privileged and unprivileged stack pointers are initialised. -1. Execution is de-privileged and handed over to the unprivileged code. + - Secure boxes are loaded. For each of them: + - The `bss` section is zeroed. + - Access Control Lists (ACLs) are registered. + - Stacks are initialized. + - A private box context is initialized, if required. + - The MPU (ARM or third-party) is configured. +1. Privileged and unprivileged stack pointers are initialized. +1. Execution is deprivileged and handed over to the unprivileged code. -From this moment on, the operating system/application runs in unprivileged mode and in the default context, which is the one of the public box. +From this moment on, the operating system/application runs in unprivileged mode and in the default context, which is that of the public box. ### Context switching -The uVisor is able to set up a secure execution environment for itself and for each configured secure box. Whenever an event or an explicit call must be run in a specific environment, a context switch is triggered. +The uVisor can set up a secure execution environment for itself and for each configured secure box. Whenever an event or an explicit call must run in a specific environment, a context switch is triggered. During a context switch, the uVisor stores the state of the previous context and then: -* It re-configures the stack pointer and the box context pointer. -* It re-configures the MPU and the peripherals protection. -* It hands the execution to the target context. +- Reconfigures the stack pointer and the box context pointer. +- Reconfigures the MPU and the peripherals protection. +- Hands the execution to the target context. A context switch is triggered automatically every time the target of a function call or exception handling routine (interrupts) belongs to a different secure domain. This applies to user interrupt service routines, threads and direct function calls. diff --git a/docs/README.md b/docs/README.md index a0e72aad..4fd49191 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,25 +1,25 @@ # The uVisor Documentation -The uVisor documentation is divided into two areas, depending on what your interest. These domains are described below. If you instead are interested in the general uVisor design philosophy, you can see our [high level introduction to mbed uVisor](https://github.com/ARMmbed/uvisor/raw/docs/uVisorSecurity-TechCon2016.pdf) or refer to the uVisor [GitHub documentation](../README.md). +The uVisor documentation consists of two sections, API documentation and core documentation. You can find descriptions of these domains below. If you instead are interested in the general uVisor design philosophy, see the [high-level introduction to mbed uVisor](https://github.com/ARMmbed/uvisor/raw/docs/uVisorSecurity-TechCon2016.pdf) or refer to the uVisor [GitHub documentation](../README.md). ## API documentation -These are the user-facing documents. They are the ideal starting point to know what you can do with uVisor, and how to setup an application to use the uVisor features. +These user-facing documents show what you can do with uVisor and how to set up an application that uses the uVisor features. | I want to... | Document | |-------------------------------------------------------|--------------------------------------------------------------| -| Get a high level introduction to mbed uVisor | [Practical real-time operating system security for the masses](https://github.com/ARMmbed/uvisor/raw/docs/uVisorSecurity-TechCon2016.pdf) | -| Start using uVisor in mbed OS on a supported platform | [Quick-Start Guide for uVisor on mbed OS](api/QUICKSTART.md) | +| See a high-level introduction to mbed uVisor | [Practical real-time operating system security for the masses](https://github.com/ARMmbed/uvisor/raw/docs/uVisorSecurity-TechCon2016.pdf) | +| Start using uVisor in mbed OS on a supported platform | [Getting started guide for uVisor on mbed OS](api/QUICKSTART.md) | | Read the uVisor API documentation in detail | [The uVisor API documentation](api/API.md) | | Enable the uVisor debug messages | [Debugging uVisor on mbed OS](api/DEBUGGING.md) | ## Core documentation -These documents describe the uVisor internals more in details. They are useful if you need to contribute to uVisor, compile a specific uVisor version, or if you just want to know more about the uVisor core. +These documents describe the uVisor internals in more detail. They are useful if you want to contribute to uVisor, compile a specific uVisor version or just know more about the uVisor core. | I want to... | Document | |----------------------------------|-------------------------------------------------------------------------------------------------------| | Understand uVisor integration | [mbed uVisor integration in mbed OS](https://github.com/ARMmbed/uvisor/raw/docs/uvisor-rtos-docs.pdf) | -| Port uVisor to my platform/OS | [uVisor Porting Guide for mbed OS](core/PORTING.md) | -| Test and experiment with uVisor | [Developing with uVisor Locally on mbed OS](core/DEVELOPING_LOCALLY.md) | -| Contribute to uVisor | [Contributing to uVisor](../CONTRIBUTING.md) | +| Port uVisor to my platform/OS | [uVisor porting guide for mbed OS](core/PORTING.md) | +| Test and experiment with uVisor | [Developing with uVisor locally on mbed OS](core/DEVELOPING_LOCALLY.md) | +| Contribute to uVisor | [Contributing to uVisor](../CONTRIBUTING.md) \ No newline at end of file diff --git a/docs/api/API.md b/docs/api/API.md index c65cd80d..203a0bdd 100644 --- a/docs/api/API.md +++ b/docs/api/API.md @@ -1,14 +1,7 @@ # uVisor API documentation -Here you can find detailed documentation for: - -1. [Configuration macros](#configuration-macros), to configure a secure box and protect data and peripherals. -1. [Box Identity](#box-identity), to retrieve a box-specific ID or the namespace of the current or calling box. -1. [Low level APIs](#low-level-apis), to access uVisor functions that are not available to unprivileged code (interrupts, restricted system registers). -1. [Type definitions](#type-definitions). -1. [Error codes](#error-codes). - ## Configuration macros +You can use configuration macros to configure a secure box and protect data and peripherals. ```C UVISOR_BOX_CONFIG(box_name @@ -24,7 +17,7 @@ UVISOR_BOX_CONFIG(box_name Type - C/C++ pre-processor macro (pseudo-function) + C/C++ preprocessor macro (pseudo-function) Parameters @@ -46,6 +39,7 @@ UVISOR_BOX_CONFIG(box_name Example: + ```C #include "uvisor-lib/uvisor-lib.h" @@ -107,11 +101,7 @@ Example: UVISOR_SET_MODE(UVISOR_ENABLED); ``` -**Note:** - -1. This macro is only needed temporarily (uVisor disabled by default) and will be removed in the future. - -2. This macro must be used only once in the top level yotta executable. +**Note:** This macro is only temporary (uVisor disabled by default) and will be removed in the future. --- @@ -161,11 +151,7 @@ static const UvBoxAclItem g_background_acl[] = { UVISOR_SET_MODE_ACL(UVISOR_ENABLED, g_background_acl); ``` -**Note:** - -1. This macro is only needed temporarily (uVisor disabled by default) and will be removed in the future. - -2. This macro must be used only once in the top level yotta executable. +**Note:** This macro is only temporary (uVisor disabled by default) and will be removed in the future. --- @@ -178,15 +164,15 @@ UVISOR_BOX_NAMESPACE(static const char const namespace[]) Description

Specify the namespace for a box.

-

The namespace of the box must be a null-terminated string no longer than MAX_BOX_NAMESPACE_LENGTH (including the terminating null). The namespace must also be stored in public flash. uVisor will verify that the namespace is null-terminated and stored in public flash at boot-time, and will halt if the namespace fails this verification.

+

The namespace of the box must be a null-terminated string no longer than MAX_BOX_NAMESPACE_LENGTH (including the terminating null). The namespace must also be stored in public flash. uVisor will verify that the namespace is null-terminated and stored in public flash at boot-time and will halt if the namespace fails this verification.

For now, use a reverse domain name for the box namespace. If you don't have a reverse domain name, use a GUID string identifier. We currently don't verify that the namespace is globally unique, but we will perform this validation in the future.

-

Use of this configuration macro before UVISOR_BOX_CONFIG is required. If you do not wish to give your box a namespace, specify NULL as the namespace to create an anonymous box.

+

You must use this configuration macro before UVISOR_BOX_CONFIG. If you do not wish to give your box a namespace, specify NULL as the namespace to create an anonymous box.

Type - C/C++ pre-processor macro (pseudo-function) + C/C++ preprocessor macro (pseudo-function) Parameters @@ -204,17 +190,16 @@ UVISOR_BOX_NAMESPACE("com.example.my-box-name"); UVISOR_BOX_CONFIG(my_box_name, UVISOR_BOX_STACK_SIZE); ``` -## Box Identity - +## Box identity A box identity identifies a security domain uniquely and globally. -The box identity API can be used to determine the source box of an inbound secure gateway call. This can be useful for implementing complex authorization logic between mutually distrustful security domains. +You can use the box identity API to determine the source box of an inbound secure gateway call. This can be useful for implementing complex authorization logic between mutually distrustful security domains. uVisor provides the ability to retrieve the box ID of the current box (`uvisor_box_id_self`), or of the box that called the current box through an RPC gateway via the `box_id_caller` parameter of `rpc_fncall_waitfor`. -The box ID number is not constant and can change between reboots. But, the box ID number can be used as a token to retrieve a constant string identifier, known as the box namespace. +The box ID number is not constant and can change between reboots, but you can use it as a token to retrieve a constant string identifier, known as the box namespace. -A box namespace is a static, box-specific string, that can help identify which box has which ID at run-time. In the future, the box namespace will be guaranteed to be globally unique. +A box namespace is a static, box-specific string that can help identify which box has which ID at runtime. In the future, the box namespace will be globally unique. A full example using this API is available at [mbed-os-example-uvisor-number-store](https://github.com/ARMmbed/mbed-os-example-uvisor-number-store). @@ -250,7 +235,7 @@ int rpc_fncall_waitfor(const TFN_Ptr fn_ptr_array[], size_t fn_count, int * box_ -> When deciding which memory to provide for `rpc_fncall_waitfor` to use when writing `box_id_caller`, strongly prefer thread local storage when multiple threads in a box can handle incoming RPC. +> When deciding which memory to provide for `rpc_fncall_waitfor` to use when writing `box_id_caller`, use thread local storage when multiple threads in a box can handle incoming RPC. --- @@ -284,9 +269,7 @@ int uvisor_box_namespace(int box_id, char *box_namespace, size_t length) ## Low-level APIs -Currently the following low level operations are permitted: - -1. Interrupt management. +You can use low-level APIs to access uVisor functions that are not available to unprivileged code (interrupts, restricted system registers). The only permitted low-level operation is interrupt management. ### Interrupt management @@ -297,7 +280,7 @@ void vIRQ_SetVector(uint32_t irqn, uint32_t vector) - + @@ -305,7 +288,7 @@ void vIRQ_SetVector(uint32_t irqn, uint32_t vector) -
DescriptionSet an ISR for IRQnRegister an ISR to the currently active box
Parameters
uint32_t irqn
uint32_t vector
Interrupt handler + Interrupt handler; if 0 the IRQn slot is deregistered for the current box
@@ -486,7 +469,7 @@ int vIRQ_GetLevel(void) ## Type definitions ```C -typedef uint32_t UvisroBoxAcl; /* Permssion mask */ +typedef uint32_t UvisroBoxAcl; /* Permission mask */ ``` --- @@ -511,4 +494,4 @@ typedef struct | `FAULT_BUS` | 6 | | `FAULT_USAGE` | 7 | | `FAULT_HARD` | 8 | -| `FAULT_DEBUG` | 9 | +| `FAULT_DEBUG` | 9 | \ No newline at end of file diff --git a/docs/api/DEBUGGING.md b/docs/api/DEBUGGING.md index 1159fe93..cd1b5a47 100644 --- a/docs/api/DEBUGGING.md +++ b/docs/api/DEBUGGING.md @@ -1,30 +1,30 @@ # Debugging uVisor on mbed OS -The uVisor is distributed as a pre-linked binary blob. Blobs for different mbed platforms are released in the mbed OS repository, [ARMmbed/mbed-os](https://github.com/ARMmbed/mbed-os), and are linked to your application when you build it. Two classes of binary blobs are released for each version — one for release and one for debug. +uVisor is distributed as a prelinked binary blob. Blobs for different mbed platforms are released in the mbed OS repository, [ARMmbed/mbed-os](https://github.com/ARMmbed/mbed-os), and are linked to your application when you build it. Two classes of binary blobs are released for each version — one for release and one for debug. -If you only want to use the uVisor debug features on an already supported platform, you do not need to clone it and build it locally. If you instead want to make modifications to uVisor (or port it to your platform) and test the modifications locally with your app, please follow the [Developing with uVisor Locally on mbed OS](../core/DEVELOPING_LOCALLY.md) guide first. +If you want to use the uVisor debug features on an already supported platform, you do not need to clone it and build it locally. If you instead want to make modifications to uVisor (or port it to your platform) and test the modifications locally with your app, please follow the [Developing with uVisor locally on mbed OS](../core/DEVELOPING_LOCALLY.md) guide first. -In this quick guide we will show you how to enable the default debug features on uVisor, and how to instrument it to get even more debug information. You will need the following: +This guide will show you how to enable the default debug features on uVisor and how to use it to get even more debug information. You will need: -* A GDB-enabled board (and the related tools). -* A [target supported](../../README.md#supported-platforms) by uVisor on mbed OS. If your target is not supported yet, you can follow the [uVisor Porting Guide for mbed OS](../core/PORTING.md). -* The Launchpad [GCC ARM Embedded](https://launchpad.net/gcc-arm-embedded) toolchain. -* GNU make. -* git. +- A GDB-enabled board (and the related tools). +- A [target](../../README.md#supported-platforms) uVisor supports on mbed OS. If uVisor does not support your target yet, you can follow the [uVisor porting guide for mbed OS](../core/PORTING.md). +- The Launchpad [GNU ARM Embedded](https://launchpad.net/-arm-embedded) Toolchain. +- GNU Make. +- Git. ## Debug capabilities The uVisor provides two main sources of debug information: -1. Runtime messages. These are delivered through [semihosting](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0471l/pge1358787045051.html), which requires a debugger to be connected to the device. Currently debug messages are used to instrument some of the security-critical features of uVisor, like boot and start-up configuration, interrupts management, and context switching. A post-mortem screen is also output when the system is halted due to a fault. +- Runtime messages. These are delivered through [semihosting](http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0471l/pge1358787045051.html), which requires a debugger to be connected to the device. Currently, debug messages implement some of the security-critical features of uVisor, such as boot and startup configuration, interrupts management and context switching. A postmortem screen is also output when the system halts due to a fault. -1. Debug box drivers. We call a *debug box* a secure box that registers with uVisor to handle debug events and messages. The uVisor provides a predefined function table that describes the driver and its capabilities. Different debug boxes can implement these handlers differently, independently from uVisor. All handlers are executed in unprivileged mode. +- Debug box drivers. We call a secure box that registers with uVisor to handle debug events and messages a *debug box*. The uVisor provides a predefined function table that describes the driver and its capabilities. Different debug boxes can implement these handlers differently, independently from uVisor. All handlers are executed in unprivileged mode. -Runtime messages and debug box handlers are independent from each other. Even if an application does not include a debug box, the uVisor is still able to deliver basic runtime messages. Conversely, an application that includes a debug box will handle debug events even if the release build of uVisor is used and possibly even without a debugger connected. +Runtime messages and debug box handlers are independent from each other. Even if an application does not include a debug box, the uVisor can deliver basic runtime messages. Conversely, an application that includes a debug box will handle debug events even if you use the release build of uVisor and possibly even without a debugger connected. ## Enabling runtime messages -If you want to observe the uVisor runtime messages you need to have a debugger connected to your board. We will use our Hello World application for this guide, built for the NXP FRDM-K64F target: +If you want to observe the uVisor runtime messages, connect a debugger to your board. Use the Hello World application for this guide, which is built for the NXP FRDM-K64F target: ```bash $ cd ~/code @@ -32,26 +32,25 @@ $ mbed import https://github.com/ARMmbed/mbed-os-example-uvisor $ cd mbed-os-example-uvisor ``` -Of course any other application can be used, provided that it is correctly set up to use uVisor. See [Developing with uVisor Locally on mbed OS](../core/DEVELOPING_LOCALLY.md) for more details. -Runtime messages are silenced by default. In order to enable them, you need to build your application linking to the debug version of uVisor. The uVisor libraries that we publish in mbed OS provide both the release and debug builds of uVisor, so you only need to run the following command: +You can also use any other application that is set up to use uVisor. See [Developing with uVisor locally on mbed OS](../core/DEVELOPING_LOCALLY.md) for more details. Runtime messages are silenced by default. In order to enable them, you need to build your application linking to the debug version of uVisor. The uVisor libraries that we publish in mbed OS provide both the release and debug builds of uVisor, so you only need to run the following command: ```bash $ mbed compile -m ${your_target} -t GCC_ARM -c --profile mbed-os/tools/profiles/debug.json ``` -The `--profile mbed-os/tools/profiles/debug.json` option ensures that the build system enables debug symbols and disables optimizations. In addition, it ensures that the debug build of uVisor is selected, which enables the uVisor runtime messages. +The `--profile mbed-os/tools/profiles/debug.json` option ensures that the build system enables debug symbols and disables optimizations. In addition, it ensures the selection of the debug build of uVisor, which enables the uVisor runtime messages. -Now start the GDB server. This step changes depending on which debugger you are using. In case you are using a J-Link debugger, run: +Now start the GDB server. This step changes depending on which debugger you are using. If you are using a J-Link debugger, run: ```bash $ JLinkGDBServer -device ${device_name} -if ${interface} ``` -In the command above `${device_name}` and `${interface}` are J-Link-specific. Please check out the [J-Link documentation](https://www.segger.com/admin/uploads/productDocs/UM08001_JLink.pdf) and the list of [supported devices](https://www.segger.com/jlink_supported_devices.html). +In the command above, `${device_name}` and `${interface}` are J-Link-specific. Please see the [J-Link documentation](https://www.segger.com/admin/uploads/productDocs/UM08001_JLink.pdf) and the list of [supported devices](https://www.segger.com/jlink_supported_devices.html). -Time to flash the device! We will use GDB for that, even if your device allows drag & drop flashing. This allows us to potentially group several commands together into a start-up GDB script. +To flash the device, use GDB, even if your device allows drag and drop flashing. This allows you to potentially group several commands together into a startup GDB script. -You need to connect to the GDB server for that. Here we use `arm-none-eabi-gdb`, which comes with the Launchpad GCC ARM Embedded toolchain. Other equivalent tools will work similarly but are not covered by this guide. +Connect to the GDB server. You can use `arm-none-eabi-gdb`, which comes with the Launchpad GNU ARM Embedded Toolchain. Other equivalent tools will work similarly. ``` $ arm-none-eabi-gdb @@ -76,7 +75,7 @@ The following is the minimum set of commands you need to send to the device to f (gdb) file BUILD/${target}/${your_app}.elf ``` -From here on if you send the `c` command the program will run indefinitely. Of course you can configure other addresses/ports for the target. Please refer to the [GDB documentation](http://www.gnu.org/software/gdb/documentation/) for details on the GDB commands. +From here on, if you send the `c` command, the program will run indefinitely. Of course, you can configure other addresses and ports for the target. Please refer to the [GDB documentation](http://www.gnu.org/software/gdb/documentation/) for details about the GDB commands. You can also group these commands in a script and pass it directly to `arm-none-eabi-gdb`: @@ -90,39 +89,39 @@ You can observe the debug messages using `netcat` or any other equivalent progra $ nc localhost 2333 ``` -Similarly to the GDB server, the port can be changed, if you want. +As with the GDB server, you can change the port if you want. -Currently the following messages are printed: +The following messages are printed: -* Start-up and initialization routines. -* Runtime assertions (failed sanity checks). -* vIRQ operations: Registering, enabling, disabling, and releasing interrupts. -* Faults: For all type of faults a default blue screen is printed. It contains the following information: - * MPU configurations. - * Relevant fault status registers. - * Faulting address. - * Exception stack frame. -* Specific fault handlers might include additional information relevant to the fault. +- Startup and initialization routines. +- Runtime assertions (failed sanity checks). +- vIRQ operations: Registering, enabling, disabling and releasing interrupts. +- Faults: For all type of faults, a default blue screen is printed. It contains the following information: + - MPU configurations. + - Relevant fault status registers. + - Faulting address. + - Exception stack frame. +- Specific fault handlers might include additional information relevant to the fault. ### Limitations -* There is currently only one level of verbosity available, which prints all possible messages. +- There is currently only one level of verbosity available, which prints all possible messages. -* Debug messages are functionally blocking, meaning that if uVisor runs with debug enabled and a debugger is not connected the system will halt waiting for a semihosting connection. +- Debug messages are functionally blocking, meaning that if uVisor runs with debug enabled and a debugger is not connected, the system will halt and wait for a semihosting connection. -* Debug messages might interfere with timing constraints, as they are shown while running in the highest priority level. Applications that have very strict timing requirements might show some unexpected behaviour. +- Debug messages might interfere with timing constraints because they are shown while running in the highest priority level. Applications that have strict timing requirements might show some unexpected behavior. ## The debug box -> **Warning**: The debug box feature is an early prototype. The APIs and procedures described here might change several times in non-backwards-compatible ways. +> **Warning**: The debug box feature is an early prototype. The APIs and procedures described here may change in nonbackward-compatible ways. -The uVisor code is instrumented to output debug information when it is relevant. In order to keep the uVisor as simple and hardware-independent as possible, some of this information is not handled and interpreted directly by uVisor. +The uVisor code is instrumented to output debug information when it is relevant. To keep the uVisor as simple and hardware-independent as possible, uVisor does not handle and directly interpret some of this information. -Instead, debug events and messages are forwarded to a special unprivileged box, called a *debug box*. A debug box is configured just like any other secure box, but it registers with uVisor to handle debug callbacks. These callbacks must adhere to a format that is provided by uVisor, in the form of a debug box driver. +Instead, debug events and messages are forwarded to a special unprivileged box, called a *debug box*. A debug box is configured just like any other secure box, but it registers with uVisor to handle debug callbacks. These callbacks must adhere to a format that uVisor provides, in the form of a debug box driver. -The debug box driver is encoded in a standard table (a C `struct`) that must be populated by a debug box at initialization time. A debug box can decide to implement only some of the available handlers, although they must all exist at least as empty functions, otherwise the program behaviour might be unpredictable. +The debug box driver is encoded in a standard table (a C `struct`) that a debug box must populate at initialization time. A debug box can decide to implement only some of the available handlers though they must all exist at least as empty functions. Otherwise, the program behavior might be unpredictable. -Currently, only one debug handler — `halt_error` — is provided. This handler only executes once, so if another fault occurs during its execution, the uVisor does not de-privilege again, halting instead. A debug box driver will also expect a `get_version()` handler in position 0 of the function table: +The debug handler — `halt_error` — only executes once, so if another fault occurs during its execution, the uVisor does not deprivilege again. It halts instead. A debug box driver also expects a `get_version()` handler in position 0 of the function table: Debug box handlers can also reset the device by calling the `NVIC_SystemReset()` API. This API cannot be called from other secure boxes. @@ -133,7 +132,7 @@ typedef struct TUvisorDebugDriver { } ``` -The following is an example of how to implement and configure a debug box. +This is an example of how to implement and configure a debug box. ```C #include "mbed.h" @@ -159,7 +158,7 @@ static void halt_error(int reason) { printf("We halted with reason %i\r\n", reason); /* If we don't do anything, the system will halt upon return. */ - /* A debug box handler like this one can also decide to reboot the whole + /* A debug box handler such as this one can also decide to reboot the whole * system. This is only allowed from the debug box. */ NVIC_SystemReset(); } @@ -179,23 +178,23 @@ static void box_debug_main(const void *) ## Platform-specific details -This section provides details on how to enable debug on some specific hardware platforms. +This section provides details about how to enable debug on specific hardware platforms. -#### NXP FRDM-K64F +### NXP FRDM-K64F -The board features both a GDB-enabled on-board USB controller and a JTAG port. If you want to use the on-board USB, you must make sure to have the latest bootloader with the OpenSDA v2.0 firmware. +The board features both a GDB-enabled on-board USB controller and a JTAG port. To use the on-board USB, you must have the latest bootloader with the OpenSDA v2.0 firmware: -* [OpenSDA bootloader](http://www.nxp.com/products/software-and-tools/run-time-software/kinetis-software-and-tools/ides-for-kinetis-mcus/opensda-serial-and-debug-adapter:OPENSDA). -* [Instructions](https://developer.mbed.org/handbook/Firmware-FRDM-K64F) on how to re-flash the bootloader. +- [OpenSDA bootloader](http://www.nxp.com/products/software-and-tools/run-time-software/kinetis-software-and-tools/ides-for-kinetis-mcus/opensda-serial-and-debug-adapter:OPENSDA). +- [Instructions](https://developer.mbed.org/handbook/Firmware-FRDM-K64F) about how to reflash the bootloader. -The OpenSDA port provides a debugger interface which is compatible with the Segger J-Link Lite debugger. This means that you can use the [Segger J-Link tools](https://www.segger.com/jlink-software.html) and use the examples provided in this guide. +The OpenSDA port provides a debugger interface, which is compatible with the Segger J-Link Lite debugger. This means that you can use the [Segger J-Link tools](https://www.segger.com/jlink-software.html) and the examples this guide provides. -If you use the JTAG port, instead, you will need to download the tools and drivers for the debugger of your choice. +To use the JTAG port instead, download the tools and drivers for the debugger of your choice. -#### STMicorelectronics STM32F429I-DISCO +### STMicroelectronics STM32F429I-DISCO -The board provides both an on-board proprietary debugging port (ST-LINK) and a JTAG port. The latter is spread out across the GPIO pins on the board. +This board provides both an on-board proprietary debugging port (ST-LINK) and a JTAG port. The latter is spread out across the GPIO pins on the board. -If you are using ST-LINK please refer to the [STMicorelectronics website](http://www.st.com/web/catalog/tools/FM146/CL1984/SC724/SS1677/PF251168?sc=internet/evalboard/product/251168.jsp) for information on the tools and drivers needed. Please note that this debugger has not been tested with uVisor. +If you are using ST-LINK, please refer to the [STMicroelectronics website](http://www.st.com/web/catalog/tools/FM146/CL1984/SC724/SS1677/PF251168?sc=internet/evalboard/product/251168.jsp) for information about the tools and drivers you need. Please note we have not tested this debugger with uVisor. -If instead you want to connect your debugger to the JTAG port you must wire the needed pins to your connector. This [guide](https://www.segger.com/admin/uploads/evalBoardDocs/AN00015_ConnectingJLinkToSTM32F429Discovery.pdf) explains how to do that in details. The guide is specific to the J-Link connectors, but it should be easily generalized to other connectors. +If instead you want to connect your debugger to the JTAG port, you must wire the needed pins to your connector. This [guide](https://www.segger.com/admin/uploads/evalBoardDocs/AN00015_ConnectingJLinkToSTM32F429Discovery.pdf) explains how to do that in detail. The guide is specific to the J-Link connectors, but you can apply it to other connectors. \ No newline at end of file diff --git a/docs/api/QUICKSTART.md b/docs/api/QUICKSTART.md index 9160cdb5..a774dfbd 100644 --- a/docs/api/QUICKSTART.md +++ b/docs/api/QUICKSTART.md @@ -1,28 +1,28 @@ -# Quick-Start Guide for uVisor on mbed OS +# Getting started guide for uVisor on mbed OS -This guide will help you get started with uVisor on mbed OS by showing you how to create a sample application for the NXP FRDM-K64F board. +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. -The uVisor provides sandboxed environments and resources protection for applications built for ARM Cortex-M3 and Cortex-M4 devices. 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). +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). -## Overview +## Requirements -To get a basic `blinky` application running on mbed OS with uVisor enabled, you will need the following: +To run the `blinky` application on mbed OS with uVisor enabled, you need: -* 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 is only partly complete. 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). -* git. It will be used to download the mbed codebase. -* mbed-cli. You can run `pip install mbed-cli` to install it. +- 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). +- Git. +- mbed CLI. Run `pip install mbed-cli` to install it. -For the remainder of this guide we will assume the following: +The remainder of this guide assumes: -* You are developing on a \*nix machine, in the `~/code` folder. -* 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) toolchain. +- You are developing on a \*nix machine in the `~/code` folder. +- 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). You can use these instructions as guidelines in the case of other targets on other host OSs. ## Start with the `blinky` app [Go to top](#overview) -To create a new mbed application called `uvisor-example`, just run the following commands: +Create a new mbed application called `uvisor-example` by running the following commands: ```bash $ cd ~/code @@ -30,13 +30,13 @@ $ mbed new uvisor-example $ cd uvisor-example ``` -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. +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. -Once the import process is finished, create a `source` folder: +Once the import process finishes, create a `source` folder: ```bash $ mkdir ~/code/uvisor-example/source ``` -and place a new file `main.cpp` in it: +Place a new file `main.cpp` in it: ```C /* ~/code/uvisor-example/source/main.cpp */ @@ -66,25 +66,18 @@ Compile the application: $ mbed compile -m K64F -t GCC_ARM ``` -The resulting binary will be located at: +The resulting binary is located at: ```bash ~/code/uvisor-example/BUILD/K64F/GCC_ARM/uvisor-example.bin ``` -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. - ---- - -In the next sections you will see: - -* How to [enable uVisor](#enable-uvisor) on the `uvisor-example` app. -* 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. +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. ## Enable uVisor [Go to top](#overview) -To enable the uVisor on the app, add the following lines at the beginning of the `main.cpp` file: +To enable the uVisor on the app, add these lines to the beginning of the `main.cpp` file: ```C /* ~/code/uvisor-example/source/main.cpp */ @@ -114,12 +107,12 @@ UVISOR_SET_MODE_ACL(UVISOR_ENABLED, g_public_box_acls); ... ``` -In the code above we specified 2 elements: +In the code above, we specified two elements: -1. Public 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 public 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. -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 public box. +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). +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. -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: +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: ```JSON { @@ -136,57 +129,57 @@ Before compiling, we need to override the original `K64F` target to enable the u } ``` -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, we need to define them manually. +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. --- **Checkpoint** -Compile the application again. This time the `K64F` target will include the new features and labels we provided in `mbed_app.json`; +Compile the application again. This time, the `K64F` target includes the new features and labels you provided in `mbed_app.json`; ```bash $ mbed compile -m K64F -t GCC_ARM ``` -The binary will be located at: +The binary is located at: ```bash ~/code/uvisor-example/BUILD/K64F/GCC_ARM/uvisor-example.bin ``` -Reflash the device and press the reset button. The device LED should be blinking as in the previous case. +Reflash the device, and press the reset button. The device LED blinks as in the previous case. --- -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 *public box*. +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*. -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. +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. ## Add a secure box [Go to top](#overview) -Now that uVisor is enabled, we can finally add a *secure box*. +Now that uVisor is enabled, you can finally add a *secure box*. -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. +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. 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. -Instead, we provide specific APIs to instruct the uVisor to protect a private resource. We will show how to use these APIs in the `uvisor-example` app. +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. ### Configure the secure box 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. -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 2 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 is restarted. For debug purposes, the program prints the content of the buffer every time it is filled up. +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. -We want the box to have exclusive access to the following resources: +You want the box to have exclusive access to the following resources: -* The push-button peripheral (as specified by a peripheral ACL). Nobody else should be able to access the push-button port. -* The push-button interrupt (as specified by an IRQ ACL). We want the button IRQ to be rerouted to our box-specific ISR. -* The private dynamically allocated buffer (as specified by a dynamic memory ACL). -* The private variables (as specified by a static memory ACL). +- The push-button peripheral (as specified by a peripheral ACL). Nobody else should be able to access the push-button port. +- The push-button interrupt (as specified by an IRQ ACL). You want the button IRQ to reroute to our box-specific ISR. +- The private dynamically allocated buffer (as specified by a dynamic memory ACL). +- The private variables (as specified by a static memory ACL). -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_button`. +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`. ```C /* ~/code/uvisor-example/source/secure_box.cpp */ @@ -223,9 +216,9 @@ UVISOR_BOX_CONFIG(private_button, /* Name of the secure box */ ### Create the secure box's main thread function -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 whole program. +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. -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 is used to update the counter value every second. +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. ```C /* ~/code/uvisor-example/source/secure_box.cpp */ @@ -291,12 +284,12 @@ static void private_button_main_thread(const void *) A few things to note in the code above: -* If code is running in the context of `private_button`, then any object instantiated inside that code will belong 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`. -* 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. -* The `InterruptIn` object triggers the registration of an interrupt slot. Because that code is run in the context of the `private_button` 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. -* Even if the `private_button_on_press` function runs in the context of `private_button`, we 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. +- 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`. +- 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. +- The `InterruptIn` object triggers the registration of an interrupt slot. Because that code runs in the context of the `private_button` box, the push-button IRQ belongs to that box. If you want to use the IRQ APIs directly, read the [NVIC APIs section](#the-nvic-apis) below. +- 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. -> **Warning**: Instantiating an object in the `secure_box.cpp` global scope will automatically map 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`). +> **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`). --- @@ -308,13 +301,14 @@ Compile the application again: $ mbed compile -m K64F -t GCC_ARM ``` -Reflash the device, and press the reset button. The device LED should be blinking as in the previous case. +Reflash the device, and press the reset button. The device LED blinks. -If you don't see the LED blinking, it means that the application halted somewhere, probably because uVisor captured a fault. You can set up 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. +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. -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_button_on_press` function will be executed, 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. +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. ## Expose public secure entry points to the secure box +[Go to top](#overview) 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 the arguments and return values are automatically serialized using an RPC protocol to ensure no private memory can be leaked to external boxes. @@ -352,7 +346,7 @@ static int get_index() { UVISOR_BOX_RPC_GATEWAY_SYNC (private_button, secure_get_index, get_index, int, void); -#define PRIVATE_BUTTON_BUFFER_COUNT 8 + #define PRIVATE_BUTTON_BUFFER_COUNT 8 ``` ### Listening for RPC messages @@ -420,44 +414,18 @@ int main(void) 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 be increased. -## Wrap-up -[Go to top](#overview) - -In this guide we showed you how to: - -* Enable uVisor on an existing application. -* Add a secure box to your application. - * Protect static and dynamic memories in a secure box. - * Gain exclusive access to a peripheral and an IRQ in a secure box. - * Expose public secure entry points to a secure box. - -You can now modify the example or create a new one to protect your resources into a secure box. You may find the following resources useful: - -* [uVisor API documentation](API.md). -* [Debugging uVisor on mbed OS](DEBUGGING.md). - -If you found any bug or inconsistency in this guide, please [raise an issue](https://github.com/ARMmbed/uvisor/issues/new). - -## Appendix -[Go to top](#overview) - -This section contains additional information that you may find useful when setting up a secure box. +## The NVIC APIs -### The NVIC APIs - -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. - -In addition, the CMSIS header files 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. +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. 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: -* The uVisor owns the interrupt vector table. -* All ISRs are relocated to SRAM. -* The first call to any NVIC API will register the IRQ for exclusive use with the active box. IRQs cannot be unregistered. -* 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 first. -* An IRQ that belongs to a box can only be modified when that box context is active. +- The uVisor owns the interrupt vector table. +- All ISRs are relocated to SRAM. +- 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. +- An IRQ that belongs to a box can only be modified when that box context is active. -Although this behaviour is different from the original NVIC one, it is backward compatible. This means that legacy code (like a device HAL) will still work after uVisor is enabled. The general use case is the following: +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. The general use case is the following: ```C #define MY_IRQ 42 @@ -473,34 +441,22 @@ NVIC_SetPriority(MY_IRQ, 3); NVIC_EnableIRQ(MY_IRQ); ``` -> **Note**: When enabling the IRQ before setting the vector, uVisor uses the handler specified in the original vector table. This mirrors the NVIC hardware behavior. - -For more information on the uVisor APIs, see the [uVisor API documentation](API.md) document. - -### The *public box* ACLs +> **Note**: In this model, a call to `NVIC_SetVector` must happen before an IRQ state changes. In platforms that don't relocate the interrupt vector table, such a call might be absent and must be added to work with uVisor. -The code samples that we provide in this guide give you a ready-made list of ACLs for the public 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. +## The *public box* ACLs -This section shows how to discover the needed ACLs for the public 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. +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. -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. - -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: +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: ```C static const UvisorBoxAclItem g_public_box_acls[] = { } ``` -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: - -```bash -$ mbed compile -m K64F -t GCC_ARM --profile mbed-os/tools/profiles/debug.json -``` +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. -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. - -Once the uVisor debug messages are enabled, you will see your application fail. The failure is due to the first missing ACL being hit by the public box code. The message will look like: +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: ``` *********************************************************** @@ -509,7 +465,7 @@ Once the uVisor debug messages are enabled, you will see your application fail. ... -* MEMORY MAP +/* MEMORY MAP Address: 0x4004800C Region/Peripheral: SIM Base address: 0x40047000 @@ -518,8 +474,7 @@ Once the uVisor debug messages are enabled, you will see your application fail. ... ``` -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: - +Once you know which peripheral is causing the fault (the `SIM` peripheral, in this example), add its entry to the ACLs list: ```C static const UvisorBoxAclItem g_public_box_acls[] = { @@ -527,8 +482,16 @@ static const UvisorBoxAclItem g_public_box_acls[] = { }; ``` -> **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. +> **Note**: If the fault debug screen does not show the name of the peripheral, look it up in the target device reference manual. -For readability, do not use the hard-coded addresses of your peripherals, but rather use the symbols that the target CMSIS module provides. +For readability, do not use the hard-coded addresses of your peripherals. Instead, use the symbols that the target CMSIS module provides. -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. +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. + +## Additional resources +[Go to top](#overview) + +- [uVisor API documentation](API.md). +- [Debugging uVisor on mbed OS](DEBUGGING.md). + +If you found any bug or inconsistency in this guide, please [raise an issue](https://github.com/ARMmbed/uvisor/issues/new). diff --git a/docs/api/manual/UseCases.md b/docs/api/manual/UseCases.md index fe08ee9f..84d06569 100644 --- a/docs/api/manual/UseCases.md +++ b/docs/api/manual/UseCases.md @@ -1,51 +1,46 @@ -# High Level Design Overview +# High-level design overview -## Memory Allocation System +## Memory allocation system -Although many processes or secure domains might be fine with just using statically allocated memories, there's a need for reliable, safe and secure memory allocation. This need for dynamic memory allocation is difficult to satisfy on Cortex-M microcontrollers due to the lack of a Memory Management Unit (MMU). +Although many processes and secure domains can use statically allocated memories, there's a need for reliable, safe and secure dynamic memory allocation. This need is difficult to satisfy on Cortex-M microcontrollers due to the lack of a Memory Management Unit (MMU). -To avoid memory fragmentation uVisor uses a nested memory allocator. +To avoid memory fragmentation, uVisor uses a nested memory allocator. -### Top Level Memory Allocation +### Top-level memory allocation -On the top level three methods exist for allocating memories for a process or thread: +On the top level, two methods exist for allocating memories for a process or thread: -- One static memory region per security context as implemented by uVisor today. The allocation happens during link time and can be influenced by compile time box configuration options. This region contains the heap and stack memories. -- After subtracting the per-process memories and the global stack, the remaining memory is split into a coarse set of equally sized large memory pages. For an instance it might make sense to split a 64kb large SRAM block into 8kb pool memory chunks. +- One static memory region per security context as implemented by uVisor today. The allocation happens during link time, and compile time box configuration options can affect it. This region contains the heap and stack memories. +- After subtracting the per-process memories and the global stack, the remaining memory is split into a coarse set of equally sized large memory pages. For example, it may make sense to split a 64 KB SRAM block into 8 KB pool memory chunks. -The recommended operation for a process is to keep static memory consumption as low as possible. For occasional device operations with large dynamic memory consumption, the corresponding process temporarily allocates one or more pages of from the memory pool. +The recommended operation for a process is to keep static memory consumption as low as possible. For occasional device operations with large dynamic memory consumption, the corresponding process temporarily allocates one or more pages from the memory pool. -### Tier-1 Page Allocator +### Tier-1 page allocator -The tier-1 page allocator hands out pages and secures access to them. It is part of the uVisor core functionality, and therefore is only accessible via the SVC-based uVisor API. +The tier-1 page allocator distributes pages and secures access to them. It is part of the uVisor core functionality and therefore only accessible via the SVC-based uVisor API. -On boot, uVisor initializes the non-statically allocated heap memory into correctly aligned and equally-sized memory pages. -The page size is known at compile time, however, the number of pages is known only to the allocator and only at runtime, due to alignment requirements. +On boot, uVisor initializes the nonstatically allocated heap memory into aligned and equally sized memory pages. The page size is known at compile time; however, only the allocator knows the number of pages and only at runtime, due to alignment requirements. -#### Choosing a Page Size +#### Choosing a page size -The requested page size is passed through the uVisor input section and read by the page allocator initializer. -You may overwrite the default page size of 16kB by passing the `UVISOR_PAGE_SIZE` macro with the value in bytes to the compiler. +The requested page size passes through the uVisor input section, and the page allocator initializer reads it. You may overwrite the default page size of 16 KB by passing the `UVISOR_PAGE_SIZE` macro with the value in bytes to the compiler. -Note, that uVisor is only able to secure up to 16 pages by default (configurable during porting). -It is therefore recommended to keep the page size as large as feasible, taking into account the largest continuous memory allocation that your application requires as well as keeping the total number of available pages small. +Note that uVisor is only able to secure up to 16 pages by default (configurable during porting). We therefore recommend you keep the page size as large as feasible, taking into account the largest continuous memory allocation that your application requires as well as keeping the total number of available pages small. -The page size must be larger than 1kB and smaller than 512MB and must be a power-of-two to work with the ARMv7-MPU alignment restrictions. +The page size must be larger than 1 KB and smaller than 512 MB and must be a power of two to work with the ARMv7-MPU alignment restrictions. The page allocator verifies the page size for correct alignment. -The page allocator will verify the page size for correct alignment. - -#### Requesting Pages +#### Requesting pages ```C int uvisor_page_malloc(UvisorPageTable * const table); ``` -A process can request pages by passing a page table to the allocator containing the number of required pages, the required page size as well as an array of page origins. -Note that the tier-1 allocator does not allocate any memory for the page table, but works directly on the memory the user provided. -If the tier-2 allocator requests 5 pages, it needs to make sure the page table is large enough to hold 5 page origins! -This mechanism allows the passing statically as well as dynamically allocated page tables. +A process can request pages by passing a page table to the allocator containing the number of required pages, the required page size and an array of page origins. Note that the tier-1 allocator does not allocate any memory for the page table but works directly on the memory the user provides. + +If the tier-2 allocator requests five pages, it needs to make sure the page table is large enough to hold five page origins. This mechanism allows the passing of statically as well as dynamically allocated page tables. A page table structure looks like this: + ```C typedef struct { uint32_t page_size; /* The page size in bytes. */ @@ -54,46 +49,42 @@ typedef struct { } UvisorPageTable; ``` -If enough free pages are available, the allocator will mark them as in use, zero them, and write each page origin into the page table. +If enough free pages are available, the allocator will mark them as in use, zero them and write each page origin into the page table. -The allocator returns an error code, if the page table is not formatted correctly. -The requested `page_size` must be a equal to `UVISOR_PAGE_SIZE`, and all of the -page table memory must be owned by the calling security context. +The allocator returns an error code if the page table is not formatted correctly. The requested `page_size` must be equal to `UVISOR_PAGE_SIZE`, and the calling security context must own all of the page table memory. -Note that the returned pages are **not** guaranteed to be allocated consecutively. -It is the responsibility of the tier-2 allocator to make sure that memory requests for continous memory, that exceed the requested page size, are blocked. +Note that there is **no** guarantee of the consecutive allocation of returned pages. It is the responsibility of the tier-2 allocator to make sure that memory requests for continuous memory that exceed the requested page size are blocked. -#### Freeing Pages +#### Freeing pages ```C int uvisor_page_free(const UvisorPageTable * const table); ``` -A process can free pages by passing a page table to the allocator. -The allocator first checks the validity of the page table, to make sure the pages are owned by the calling security context and then returns the pages to the pool. + +A process can free pages by passing a page table to the allocator. The allocator first checks the validity of the page table to make sure the calling security context owns the pages and then returns the pages to the pool. Hint: Use the page table that was returned on allocation. -### Tier-2 Memory Allocator +### Tier-2 memory allocator + +The tier-2 memory allocator provides a common interface to manage memory backed by tier-1 allocated pages or statically allocated memory. The tier-2 allocator is part of the uVisor library, and calls to it do not require a secure gateway because it can only operate on memory the process owns. -The tier-2 memory allocator provides a common interface to manage memory backed by tier-1 allocated pages or statically allocated memory. The tier-2 allocator is part of the uVisor library and calls to it does not require a secure gateway, since it can only operate on memory owned by the process anyway. +The memory pool contains all memory management data, and its overhead depends on the management algorithm. An allocator handle is an opaque pointer. -All memory management data is contained within the memory pool and its overhead depends on the management algorithm. -An allocator handle is a simple opaque pointer. ```C typedef void* SecureAllocator; ``` -#### Initializing Static Memory +#### Initializing static memory Statically allocated heap memories can be initialized using this method: + ```C SecureAllocator secure_allocator_create_with_pool( void* mem, /**< origin address of pool */ @@ -102,7 +93,7 @@ SecureAllocator secure_allocator_create_with_pool( The uVisor box heap is initialized using this method on first call to `malloc`. -#### Initializing Page-Backed Memory +#### Initializing page-backed memory ```C SecureAllocator secure_allocator_create_with_pages( @@ -110,67 +101,57 @@ SecureAllocator secure_allocator_create_with_pages( size_t max_heap_alloc); /**< maximum continuous heap allocation */ ``` -The tier-2 allocator computes the required page size and page count from the `heap` size, taking account the requirement that the `max_heap_alloc` needs to be placed in one continuous memory section. +The tier-2 allocator computes the required page size and page count from the `heap` size, taking into account the requirement that the `max_heap_alloc` needs to be placed in one continuous memory section. -For example: With a heap size of 12kB (6kB continuous) and a physical page size of 8kB, two non-consecutive pages would satisfy this requirement. -However, if instead of 6kB we would need 9kB of continuous heap allocation, we would require one page of 16kB, ie. two consecutive 8kB pages. This is not be guaranteed by the tier-1 allocator, therefore the physical page size needs to be increased to 16kB for this allocation to succeed. +For example: With a heap size of 12 KB (6 KB continuous) and a physical page size of 8 KB, two nonconsecutive pages would satisfy this requirement. However, if instead of 6 KB you need 9 KB of continuous heap allocation, you would require one page of 16 KB, in other words, two consecutive 8 KB pages. The tier-1 allocator does not guarantee this; therefore, the physical page size needs to be increased to 16 KB for this allocation to succeed. -Note that no guarantee can be made that the maximum continuous heap allocation will be available to the caller. -This depends on the fragmentation properties of the tier-2 memory management algorithm. -This computation only makes it physically possible to perform such a continuous allocation! +Note that there is no guarantee that the maximum continuous heap allocation will be available to the caller. This depends on the fragmentation properties of the tier-2 memory management algorithm. This computation only makes it physically possible to perform such a continuous allocation. Note that the memory for the page table is dynamically allocated inside the process heap! -#### Allocator Lifetime +#### Allocator lifetime -An allocator for static memory cannot be destroyed during program execution. -Attempting to do so will result in an error. +An allocator for static memory cannot be destroyed during program execution. Attempting to do so will result in an error. -An allocator for page-backed memory is bound to a process thread. It must not be destroyed while the thread is still running. -Other threads within the same process are not allowed to allocate inside the page-backed memory of another thread. -However, they may of course write and read into the page-backed memory of another thread, but that thread must first allocate memory for it and pass the pointer to the other thread. +An allocator for page-backed memory is bound to a process thread. It must not be destroyed while the thread is still running. Other threads within the same process are not allowed to allocate inside the page-backed memory of another thread. However, they may of course write and read into the page-backed memory of another thread, but that thread must first allocate memory for it and pass the pointer to the other thread. -This restriction ensures that when a thread finishes execution, it is safe to return all its pages to the memory pool, without the risk of deleting an allocation from another thread. -It also removes the need to keep track which thread allocated in what page. +This restriction ensures that when a thread finishes execution, it is safe to return all its pages to the memory pool without the risk of deleting an allocation from another thread. It also removes the need to keep track of which thread is allocated in which page. ```C int secure_allocator_destroy(SecureAllocator allocator); ``` -#### Memory Management +#### Memory management + +Three functions manage memory in a process: -Three functions are provided to manage memory in a process: ```C void * secure_malloc(SecureAllocator allocator, size_t size); void * secure_realloc(SecureAllocator allocator, void * ptr, size_t size); void secure_free(SecureAllocator allocator, void * ptr); ``` -These functions simply multiplex the `malloc`, `realloc` and `free` to chosen allocator. -This automatically takes into account non-consecutive page tables. +These functions multiplex the `malloc`, `realloc` and `free` to the chosen allocator. This automatically takes into account nonconsecutive page tables. + +The tier-2 allocator uses the CMSIS-RTOS `rt_Memory` allocator as a back end to provide thread-safe access to memory pools. -The tier-2 allocator uses the CMSIS-RTOS `rt_Memory` allocator as a backend to provide thread-safe access to memory pools. +### Allocator management -### Allocator Management +The scheduler transparently swaps out the current allocator to provide the canonical memory management functions `malloc`, `realloc` and `free` to processes and threads. This means that calling any of these three standard memory functions automatically uses the provided allocator. The fallback scheme for this is "page-backed thread heap" -> "static process heap" -> "insecure global heap": -The current allocator is transparently swapped out by the scheduler to provide the canonical memory management functions `malloc`, `realloc` and `free` to processes and threads. -This means that calling any of these three standard memory functions, automatically -uses the provided allocator. -The fallback scheme used for this is "page-backed thread heap" -> "static process heap" -> "insecure global heap": +1. In a thread with its own page-backed heap, allocations will only be services from its own heap, not the process heap. If it runs out of memory, there is no fallback. +1. In a thread without its own page-backed heap, allocations will be serviced from the statically allocated process heap. If it runs out of memory, there is no fallback. +1. In a process with statically allocated heap, allocations will be serviced from this heap. If it runs out of memory, there is no fallback. +1. In a process without statically allocated heap, allocations will be serviced from the statically allocated insecure process heap. -1. In a thread with its own page-backed heap, allocations will only be services from its own heap, not the process heap. If it runs out of memory, no fallback is provided. -2. In a thread without its own page-backed heap, allocations will be serviced from the statically allocated process heap. If it runs out of memory, no fallback is provided. -3. In a process with statically allocated heap, allocations will be serviced from this heap. If it runs out of memory, no fallback is provided. -4. In a process without statically allocated heap, allocations will be serviced from the statically allocated insecure process heap. +A thread may force an allocation on the process heap using the `malloc_p`, `realloc_p` and `free_p` functions. This enables a worker thread with page-backed heap to store for example its final computation result on the process heap, notify its completion and then stop execution without having to wait for another thread to copy this result out of its heap. -A thread may force an allocation on the process heap using the `malloc_p`, `realloc_p` and `free_p` functions. -This enables a worker thread with page-backed heap to store for example its final computation result on the process heap, and notify its completion, and then stop execution without having to wait for another thread to copy this result out of its heap. +#### Per-thread memory allocator -#### Per-Thread Memory Allocator +All memories allocated outside of the thread will be allocated on the static heap of the process. If a thread does set the heap pointer to NULL or the heap size to zero, memory allocations are forwarded to the process's memory. -All memories allocated outside of the thread will be allocated on the static heap of the process. In case a thread does set the heap pointer to NULL or the heap size to zero, memory allocations will be forwarded to the processes memory. +Starting a thread dynamically without its own heap will fall back to using the process heap: -Starting a thread dynamically without its own heap will fallback to using the process heap: ```C /* Thread is using no dynamic memory, or allocates on the process heap. */ void * thread_stack = malloc(2kB); @@ -191,7 +172,8 @@ if (thread_stack) } ``` -When starting a seldom-running, but high-memory-impact thread, the developer has the choice to tie the thread to a thread-specific heap: +When starting a seldom-running but high memory effect thread, the developer has the choice to tie the thread to a thread-specific heap: + ```C SecureAllocator thread_heap = secure_allocator_create_with_pages( 12*1024, /* Total heap size. */ @@ -219,62 +201,62 @@ if (thread_heap) { } ``` -Once the dynamic operation terminates, the threads are terminated and the corresponding memory blocks can be freed. In case allocations happened outside of the thread (using the `{malloc, realloc, free}_p` methods), these will be still around on the process heap. +Once the dynamic operation terminates, the threads are terminated, and the corresponding memory blocks can be freed. Allocations that happened outside of the thread (using the `{malloc, realloc, free}_p` methods) are still around on the process heap. -As a result memory fragmentation can effectively avoided - independent of uVisor usage. +As a result, you can effectively avoid memory fragmentation - independent of uVisor use. - +## RTOS integration mechanics -### RTOS Integration Mechanics +As currently integrated with uVisor, the RTOS runs with uVisor privileges. This must be fixed. [This issue is tracked on GitHub](https://github.com/ARMmbed/uvisor/issues/235). For now, the RTOS is a privileged component of the overall system, sharing privileged residence with uVisor. uVisor and the RTOS must trust each other because of this. Generally, uVisor is the manager of interrupts and the MPU, and the RTOS is the manager of context switching. -As currently integrated with uVisor, the RTOS runs with uVisor privileges. This must be fixed. [This issue is tracked on GitHub at -https://github.com/ARMmbed/uvisor/issues/235](https://github.com/ARMmbed/uvisor/issues/235). For now, the RTOS is a privileged component of the overall system, sharing privileged residence with uVisor. uVisor and the RTOS must trust each other because of this. Generally, uVisor is the manager of interrupts and the MPU, and the RTOS is the manager of context switching. +uVisor has three new capabilities that support RTOS integration. + - A box can specify a main thread. + - A privileged mode RTOS can call uVisor without an SVC. + - A privileged mode RTOS can specify privileged PendSV, SysTick and SVC 0 hooks in uVisor config. -Three new capabilities are added to uVisor to support RTOS integration. - - Ability for a box to specify a main thread - - Ability for a privileged mode RTOS to call uVisor without an SVC - - Ability for a privileged mode RTOS to specify privileged PendSV, SysTick, and SVC 0 hooks in uVisor config +### Ability for a box to specify a main thread -#### Ability for a box to specify a main thread +A box's code execution begins in the box's main thread. Each box has one main thread, with the exception of the public box (box 0). The box's "Box Config" specifies the function to use for that box's main thread. -A box's main thread is where code execution begins in that box. Each box has one main thread, with the exception of the public box (box 0). The box's "Box Config" specifies the function to use for that box's main thread. +The following is an example of box configuration. Note the `UVISOR_BOX_MAIN` macro, which specifies the function to use as the body of the box's main thread. -The following is an example of box configuration. Note the use of the `UVISOR_BOX_MAIN` macro to specify the function to use as the body of the box's main thread. ```C /* Pre-declaration of box main thread function */ static void example_box_main(const void *); @@ -412,72 +394,71 @@ UVISOR_BOX_CONFIG(example_box, acl, UVISOR_BOX_STACK_SIZE, box_context); The public box's code execution starts the same way that it starts when uVisor is not present. -For all boxes other than the public box: after libc, the RTOS, and C++ statically constructed objects are initialized, and the RTOS is about to start, the RTOS asks uVisor to handle the "pre-start" event. uVisor then creates main threads for each box. The main threads use the box stack as their stack. These threads do not start until after the RTOS scheduler starts (which is after pre-start is finished). +For all boxes other than the public box: after libc, the RTOS and C++ statically constructed objects are initialized and the RTOS is about to start, the RTOS asks uVisor to handle the "prestart" event. uVisor then creates main threads for each box. The main threads use the box stack as their stack. These threads do not start until after the RTOS scheduler starts (which is after prestart is finished). -#### Ability for a privileged mode RTOS to call uVisor without an SVC +### Ability for a privileged mode RTOS to call uVisor without an SVC -A privileged call mechanism (privcall) is introduced that allows privileged code to call into uVisor without performing an SVC. This mechanism is necessary because an SVC handler can't perform another SVC (unless it first deprivileges along the way, but that would be inefficient). The only client of the privcall interface is the RTOS, which is trusted. +A privileged call mechanism (`privcall`) allows privileged code to call into uVisor without performing an SVC. This mechanism is necessary because an SVC handler can't perform another SVC (unless it first deprivileges along the way, but that would be inefficient). The only client of the privcall interface is the RTOS, which is trusted. The RTOS needs to call uVisor to perform the following tasks: - - Notify uVisor of thread creation (`thread_create`) + - Notify uVisor of thread creation (`thread_create`). - uVisor uses this to track which thread belongs to which box. The box that is active when a thread is created is the box that owns that thread. - - Notify uVisor of thread destruction (`thread_destroy`) + - Notify uVisor of thread destruction (`thread_destroy`). - uVisor uses this to forget about threads it doesn't need to track anymore. - - Notify uVisor of thread switching (`thread_switch`) + - Notify uVisor of thread switching (`thread_switch`). - In this privcall, uVisor switches the box context to the owner of the thread. - - Notify uVisor that the RTOS is about to start (`pre_start`) - - uVisor will create the main threads for all boxes + - Notify uVisor that the RTOS is about to start (`pre_start`). + - uVisor creates the main threads for all boxes. -#### Ability for a privileged mode RTOS to specify privileged PendSV, SysTick, and SVC 0 hooks in uVisor config +### Ability for a privileged mode RTOS to specify privileged PendSV, SysTick and SVC 0 hooks in uVisor config -uVisor config is stored in flash, which is trusted. As such, it's the best place for privileged subsystems, like an RTOS, to register privileged handlers. uVisor config is extended to allow the specification of the following handlers via "Privileged system IRQ hooks". +uVisor config is stored in flash, which is trusted. As such, it's the best place for privileged subsystems, such as an RTOS, to register privileged handlers. uVisor config is extended to allow the specification of the following handlers via "Privileged system IRQ hooks". The Privileged system IRQ hooks can be used to specify the following handlers: - - PendSV - - RTX would register for handling PendSV to perform thread context switching - - SysTick - - RTX would register for handling SysTick (if a better periodic timer suitable for the RTX scheduler isn't available) - - SVC 0 - - RTX would register for handling SVC 0, with which RTX handles its own syscalls - + - PendSV. + - RTX would register for handling PendSV to perform thread context switching. + - SysTick. + - RTX would register for handling SysTick (if a better periodic timer suitable for the RTX scheduler isn't available). + - SVC 0. + - RTX would register for handling SVC 0, with which RTX handles its own syscalls. -### Remote Procedure Calls +## Remote procedure calls -uVisor provides a Remote Procedure Call (RPC) API to call functions that execute in the context of another box. +uVisor provides a remote procedure call (RPC) API to call functions that execute in the context of another box. -#### A General Overview of the RPC API +### A general overview of the RPC API The RPC API provides a structured way for a caller box to perform actions in a callee box. By default, boxes can't call functions in other box's contexts. A callee box declares RPC gateways to designate functions as callable by other boxes. -uVisor strictly controls the information passed between boxes, verifying that the callee box is OK with being called. This verification is done via an RPC gateway. An RPC gateway is a verifiable, in-flash data structure that the callee box uses to nominate a function as a suitable RPC target. +uVisor strictly controls the information passed between boxes, verifying that the callee box is OK with being called. This verification occurs via an RPC gateway. An RPC gateway is a verifiable, in-flash data structure that the callee box uses to nominate a function as a suitable RPC target. -If an RPC gateway exists in a callee box, then any other box can call that target function in callee box context. No other target functions can be called in a callee box other than those designated as callable via an RPC gateway. +If an RPC gateway exists in a callee box, then any other box can call that target function in callee box context. Only target functions an RPC gateway designates as callable can be called in a callee box. -##### RPC Macros +#### RPC macros -Two macros are provided to implement RPC gateways: `UVISOR_BOX_RPC_GATEWAY_SYNC` and `UVISOR_BOX_RPC_GATEWAY_ASYNC`. - - `UVISOR_BOX_RPC_GATEWAY_SYNC` creates a callable *synchronous* RPC gateway - - `UVISOR_BOX_RPC_GATEWAY_ASYNC` creates a callable *asynchronous* RPC gateway +Two macros implement RPC gateways: `UVISOR_BOX_RPC_GATEWAY_SYNC` and `UVISOR_BOX_RPC_GATEWAY_ASYNC`: + - `UVISOR_BOX_RPC_GATEWAY_SYNC` creates a callable *synchronous* RPC gateway. + - `UVISOR_BOX_RPC_GATEWAY_ASYNC` creates a callable *asynchronous* RPC gateway. -RPC gateways can be created for any function that accepts up to four, 4-byte parameters and returns up to one 4-byte value. +You can create RPC gateways for any function that accepts up to four 4-byte parameters and returns up to one 4-byte value. -Calling a synchronous RPC gateway is simple. A synchronous RPC gateway is called in the same manner that the original target function would have been called. `UVISOR_BOX_RPC_GATEWAY_SYNC` creates a gateway with an identical function signature. +Call a synchronous RPC gateway in the same manner that you would call the original target function. `UVISOR_BOX_RPC_GATEWAY_SYNC` creates a gateway with an identical function signature. -Calling an asynchronous RPC gateway is a bit more involved, as `UVISOR_BOX_RPC_GATEWAY_ASYNC` creates a gateway with a different function signature. Compared to the original target function, the return type is changed. The return type of the gateway is a token that can be used to wait for the asynchronous call. Sometimes this token may be invalid, in cases where the asynchronous call couldn't be initiated. +Calling an asynchronous RPC gateway is more involved because `UVISOR_BOX_RPC_GATEWAY_ASYNC` creates a gateway with a different function signature. The return type changes from that of the original target function. The return type of the gateway is a token you can use to wait for the asynchronous call. Sometimes this token may be invalid, in cases in which the asynchronous call couldn't be initiated. -#### Porting a library to uVisor +### Porting a library to uVisor To enable uVisor for a pre-existing library: - 1. Make a box configuration file, `secure_libraryname.cpp` - 1. Configure the box - 1. Create secure RPC gateways to designate library functions as securely callable within the newly created box - 1. Write incoming RPC handlers for all RPC target functions + 1. Make a box configuration file, `secure_libraryname.cpp`. + 1. Configure the box. + 1. Create secure RPC gateways to designate library functions as securely callable within the newly created box. + 1. Write incoming RPC handlers for all RPC target functions. -#### Creating a secure gateway for a library function +### Creating a secure gateway for a library function -We'll now work through a short example, creating both a synchronous RPC gateway and an asynchronous RPC gateway for a single library function. +This example creates both a synchronous RPC gateway and an asynchronous RPC gateway for a single library function. -Here is the imaginary function we want to make callable through RPC. +The imaginary function you want to make callable through RPC is. ```C++ /* unicorn.h */ @@ -494,22 +475,22 @@ void unicorn_barf(unicorn_barfable_t thing); The function causes a unicorn to barf up some barfable thing (imaginarily, of course). -##### Creating a synchronous RPC gateway +#### Creating a synchronous RPC gateway -To make a synchronous RPC gateway, we use the `UVISOR_BOX_RPC_GATEWAY_SYNC` macro. +To make a synchronous RPC gateway, use the `UVISOR_BOX_RPC_GATEWAY_SYNC` macro. ```C++ #define UVISOR_BOX_RPC_GATEWAY_SYNC(box_name, gw_name, fn_name, fn_ret, ...) ``` These are the parameters. - * `box_name` - The name of the target box as declared in `UVISOR_BOX_CONFIG` - * `gw_name` - The new, callable function pointer for initiating an RPC from the caller's box - * `fn_name` - The function that will run in the callee's box as an RPC target - * `fn_ret` - The return type of the function being designated as an RPC target - * `__VA_ARGS__` - The type of each parameter passed to the target function. There can be up to 4 parameters in a target function. Each parameter must be no more than uint32_t in size. If the target function accepts no arguments, pass `void` here. + - `box_name` - The name of the target box as declared in `UVISOR_BOX_CONFIG`. + - `gw_name` - The new, callable function pointer for initiating an RPC from the caller's box. + - `fn_name` - The function that will run in the callee's box as an RPC target. + - `fn_ret` - The return type of the function being designated as an RPC target. + - `__VA_ARGS__` - The type of each parameter passed to the target function. There can be up to four parameters in a target function. Each parameter must be no more than uint32_t in size. If the target function accepts no arguments, pass `void` here. -Here's how to designate the function as a synchronously callable RPC target. +Designate the function as a synchronously callable RPC target: ```C++ /* secure_unicorn.cpp */ @@ -518,30 +499,24 @@ Here's how to designate the function as a synchronously callable RPC target. UVISOR_BOX_RPC_GATEWAY_SYNC(unicorn_box, unicorn_barf_sync, unicorn_barf, void, unicorn_barfable_t); ``` -We also need to declare the gateway's function prototype, so that clients can call the freshly-created RPC gateway. Notice that the gateway creating macro made a function pointer and not a function, so we declare the gateway's function prototype as a function pointer. We also need to extern the function pointer, to let the compiler know that the gateway creating macro already created the function pointer for us. +You also need to declare the gateway's function prototype, so clients can call the freshly created RPC gateway. Notice that the gateway creating macro made a function pointer and not a function, so you declare the gateway's function prototype as a function pointer. You also need to extern the function pointer to let the compiler know that the gateway creating macro already created the function pointer for you. + ```C++ /* secure_unicorn.h */ UVISOR_EXTERN void (*unicorn_barf_sync)(unicorn_barfable_t thing); ``` -##### Creating an asynchronous RPC gateway +#### Creating an asynchronous RPC gateway -Creating the asynchronous RPC gateway is just about as easy as creating an synchronous gateway. - -To make an asynchronous RPC gateway, we use the `UVISOR_BOX_RPC_GATEWAY_ASYNC` macro. +Creating the asynchronous RPC gateway is similar to creating a synchronous gateway. To make an asynchronous RPC gateway, use the `UVISOR_BOX_RPC_GATEWAY_ASYNC` macro. ```C++ #define UVISOR_BOX_RPC_GATEWAY_ASYNC(box_name, gw_name, fn_name, fn_ret, ...) ``` -The parameters are the same as the `UVISOR_BOX_RPC_GATEWAY_SYNC` macro. - * `box_name` - The name of the target box as declared in `UVISOR_BOX_CONFIG` - * `gw_name` - The new, callable function pointer for initiating an RPC from the caller's box - * `fn_name` - The function that will run in the callee's box as an RPC target - * `fn_ret` - The return type of the function being designated as an RPC target - * `__VA_ARGS__` - The type of each parameter passed to the target function. There can be up to 4 parameters in a target function. Each parameter must be no more than uint32_t in size. If the target function accepts no arguments, pass `void` here. +The parameters are the same as those of the `UVISOR_BOX_RPC_GATEWAY_SYNC` macro. -Here's how to make the function an asynchronously callable RPC target. +Make the function an asynchronously callable RPC target: ```C++ /* secure_unicorn.cpp */ @@ -553,48 +528,47 @@ UVISOR_BOX_RPC_GATEWAY_SYNC(unicorn_box, unicorn_barf_sync, unicorn_barf, void, UVISOR_BOX_RPC_GATEWAY_ASYNC(unicorn_box, unicorn_barf_async, unicorn_barf, void, unicorn_barfable_t); ``` -Just like in the synchronous case, we again declare the the gateway's function prototype. This time, however, notice that the return value is of type `uvisor_rpc_result_t`. The return value is a token that facilitates the asynchronous calling of our gateway. +Just like in the synchronous case, you again declare the the gateway's function prototype. This time, however, notice that the return value is of type `uvisor_rpc_result_t`. The return value is a token that facilitates the asynchronous calling of your gateway. + ```C++ /* secure_unicorn.h */ UVISOR_EXTERN void (*unicorn_barf_sync)(unicorn_barfable_t thing); UVISOR_EXTERN uvisor_result_t (*unicorn_barf_async)(unicorn_barfable_t thing); ``` -That's all there is to it. That's all it takes to create an RPC gateway to a target function. +### Handling incoming RPC -#### Handling incoming RPC - -Each box has a single queue for handling incoming RPC calls. uVisor will verify the secure RPC gateways and then place calls into the target box's queue; an RPC call won't be added to the queue if the gateway isn't valid. +Each box has a single queue for handling incoming RPC calls. uVisor verifies the secure RPC gateways and then places calls into the target box's queue; an RPC call won't be added to the queue if the gateway isn't valid. Making a box capable of handling incoming RPC requires two steps. - 1. Specify the maximum number of incoming RPC calls for the box - 1. Call `rpc_fncall_waitfor` from at least one thread + 1. Specify the maximum number of incoming RPC calls for the box. + 1. Call `rpc_fncall_waitfor` from at least one thread. -##### Limits on the maximum number of outstanding RPC calls +#### Limits on the maximum number of outstanding RPC calls -As currently implemented, up to 8 RPC calls can be queued up for all RPC executors. If the executors can't execute incoming RPC calls fast enough, uVisor will prevent new RPC calls from getting queued up until space allows. +As currently implemented, up to 8 RPC calls can queue up for all RPC executors. If the executors can't execute incoming RPC calls fast enough, uVisor will prevent new RPC calls from queuing up until space allows. -So, what happens on the caller side when a callee can't handle their call? Asynchronous callers will receive a timeout if the call can't be completed quickly enough. Synchronous callers will block forever until space is available. +Asynchronous callers will receive a timeout if the call can't be completed quickly enough. Synchronous callers will block forever until space is available. -##### Executing RPC calls +#### Executing RPC calls -An RPC needs some context in which to execute. The context in which an RPC runs is designated by a call to `rpc_fncall_waitfor`. This function handles RPC calls, and then performs the RPC within its context. `rpc_fncall_waitfor` will either execute one RPC before returning or return with a status code indicating that something else happened. +An RPC needs context in which to execute. A call to `rpc_fncall_waitfor` designates the context in which an RPC runs. This function handles RPC calls and then performs the RPC within its context. `rpc_fncall_waitfor` either executes one RPC before returning or returns with a status code indicating that something else happened. -Let's have a look at this function. +Look at this function. ```C++ int rpc_fncall_waitfor(const TFN_Ptr fn_ptr_array[], size_t fn_count, int * box_id_caller, uint32_t timeout_ms); ``` -There are not so many parameters. - * `fn_ptr_array` - an array of RPC function targets that this call to `rpc_fncall_waitfor` should handle RPC to - * `fn_count` - the number of function targets in this array - * `box_id_caller` - a memory location to store the box ID of the calling box (the source box of the RPC). This is set before the RPC is dispatched, so that the RPC target function can read from this location to determine the calling box ID. Optional. - * `timeout_ms` - specifies how long to wait (in ms) for an incoming RPC calls before returning +There are not many parameters: + - `fn_ptr_array` - an array of RPC function targets that this call to `rpc_fncall_waitfor` should handle RPC to. + - `fn_count` - the number of function targets in this array. + - `box_id_caller` - a memory location to store the box ID of the calling box (the source box of the RPC). This is set before the RPC is dispatched, so the RPC target function can read from this location to determine the calling box ID. Optional. + - `timeout_ms` - specifies how long to wait (in ms) for an incoming RPC calls before returning. -And finally, the return value specifies the status of the wait (whether it timed out, or if the pool is too small, or if an RPC was handled). +The return value specifies the status of the wait (whether it timed out, if the pool is too small or if an RPC was handled). -Let's see what this would look like in practice for the unicorn library. Let's implement the body of a new thread to run in `unicorn_box` that will be used to handle RPC to the `unicorn_barf` target function. We'll have it wait forever for an incoming RPC call, handle it, and then wait for the next item. +To see what this would look like in practice for the unicorn library, implement the body of a new thread to run in `unicorn_box` that will handle RPC for the `unicorn_barf` target function. Have it wait forever for an incoming RPC call, handle it and then wait for the next item. ```C++ static void unicorn_barf_rpc_thread(const void *) @@ -617,25 +591,22 @@ static void unicorn_barf_rpc_thread(const void *) } ``` -If we wanted to handle incoming RPC calls for additional RPC targets in this same thread, we could add them to `my_fn_array`. Also, if we so desired, we could create multiple threads to wait for the same RPC targets, as might be useful in a multi-core system to handle incoming RPC calls in parallel. - -So, that about sums it up for library authors. Get out there and uVisor-enable your libraries. +If you want to handle incoming RPC calls for additional RPC targets in this same thread, you can add them to `my_fn_array`. Also, you can create multiple threads to wait for the same RPC targets, which may be useful in a multicore system to handle incoming RPC calls in parallel. -Next up is a description of how to call these gateways. +### Calling a secure gateway -#### Calling a secure gateway +Continuing with the previous example, call both a synchronous RPC gateway and an asynchronous RPC gateway. -Continuing with our previous example, we'll now work through how to call both a synchronous RPC gateway and an asynchronous RPC gateway. +#### Calling a synchronous RPC gateway -##### Calling a synchronous RPC gateway +This is the function prototype of the synchronous RPC gateway you created in the previous section. -As a convenient reminder, this is the function prototype of the synchronous RPC gateway we created in the previous section. ```C++ /* secure_unicorn.h */ UVISOR_EXTERN void (*unicorn_barf_sync)(unicorn_barfable_t thing); ``` -Calling this synchronous RPC gateway is really easy. The call looks exactly like a non-RPC to the target function. Ready? +The call for a synchronous RPC gateway looks exactly like a non-RPC to the target function. ```C++ /* example.cpp */ @@ -646,17 +617,18 @@ void example_sync(void) } ``` -Yup, that's it. That's all there is. The call will block until the target function executes and returns. If you want to only wait for a certain amount of time for the target function to return, then you'll want to use the asynchronous RPC gateway (which we'll conveniently cover right now.) +The call will block until the target function executes and returns. If you want to only wait for a certain amount of time for the target function to return, use the asynchronous RPC gateway information in the next section. + +#### Calling an asynchronous RPC gateway -##### Calling an asynchronous RPC gateway +This is the function prototype of the asynchronous RPC gateway you created in the previous section. -As another convenient reminder, this is the function prototype of the asynchronous RPC gateway we created in the previous section. ```C++ /* secure_unicorn.h */ UVISOR_EXTERN uvisor_result_t (*unicorn_barf_async)(unicorn_barfable_t thing); ``` -Now, to make the call. This isn't so different from the synchronous case, but we don't yet get the return value of the target function by calling an asynchronous RPC gateway. We instead get a `uvisor_rpc_result_t` token. +Making the call is similar to that of the synchronous case, but you don't yet get the return value of the target function by calling an asynchronous RPC gateway. You instead get a `uvisor_rpc_result_t` token. ```C++ /* example.cpp */ @@ -675,20 +647,20 @@ void example(void) /* ... */ ``` -Now the asynchronous call is initiated and we are free to do stuff asynchronously. Eventually, we'll want to wait for the call to complete. Before we get to actually waiting for the call to complete, let's introduce the function that will do the waiting for us. +Now the asynchronous call is initiated, and you are free to perform operations asynchronously. Eventually, you'll want to wait for the call to complete. This function will do the waiting for you: ```C++ int rpc_fncall_wait(uvisor_result_t result, uint32_t timeout_ms, uint32_t * ret); ``` To use `rpc_fncall_wait`, pass in: - * `result` - the result token previously received from an asynchronous call - * `timeout_ms` - a timeout in milliseconds of how long to wait for a result to come back from the RPC target function - * `ret` - a pointer to a `uint32_t`-sized return value + - `result` - the result token previously received from an asynchronous call. + - `timeout_ms` - a timeout in milliseconds of how long to wait for a result to come back from the RPC target function. + - `ret` - a pointer to a `uint32_t`-sized return value. -In our case, `unicorn_barf` has a void return value, so we can pass in `NULL` for `ret`. +In your case, `unicorn_barf` has a void return value, so you can pass in `NULL` for `ret`. -Now that we understand how to use `rpc_fncall_wait`, let's spin in a loop, waiting for the result to come back for up to 500 ms. If we don't get a result by then, we can consider the unicorn to have ran out of barf. Any unicorn worth their salt should be able to barf within 500 ms. +Now that you understand how to use `rpc_fncall_wait`, spin in a loop and wait for the result to come back for up to 500 ms. If you don't get a result by then, you can consider the unicorn to have run out of barf. ```C++ /* example.cpp */ @@ -704,8 +676,8 @@ void example(void) /* ... Do anything asynchronously here ... */ - /* Wait for a non-error result synchronously. - * Note that this wait could potentially be from a different thread. */ + /* Wait for a nonerror result synchronously. + * Note that this wait could be from a different thread. */ while (1) { int status; static const uint32_t timeout_ms = 500; @@ -718,9 +690,5 @@ void example(void) } ``` -Calling the asynchronous RPC gateway is as tough as it gets, and it isn't really that bad, is it? - -That about wraps it up for the RPC API. We've covered both how to uVisor-enable a library and how to use a uVisor-enabled library. - -#### More Information on the RPC API -For more information on the RPC API, please refer to [the well-commented RPC API C header file which is available at https://github.com/ARMmbed/uvisor/blob/master/api/inc/rpc.h](https://github.com/ARMmbed/uvisor/blob/master/api/inc/rpc.h). +### More information about the RPC API +For more information about the RPC API, please refer to [the well-commented RPC API C header file](https://github.com/ARMmbed/uvisor/blob/master/api/inc/rpc.h). diff --git a/docs/core/DEVELOPING_LOCALLY.md b/docs/core/DEVELOPING_LOCALLY.md index f6221ac9..52a91cd0 100644 --- a/docs/core/DEVELOPING_LOCALLY.md +++ b/docs/core/DEVELOPING_LOCALLY.md @@ -1,37 +1,37 @@ -# Developing with uVisor Locally on mbed OS +# Developing with uVisor locally on mbed OS -When an application uses the uVisor, mbed OS ensures that the uVisor static libraries are linked with the rest of the program. These libraries include the user-facing uVisor APIs and a pre-linked binary blob, which we call the uVisor *core*. +When an application uses uVisor, mbed OS ensures that the uVisor static libraries link with the rest of the program. These libraries include the user-facing uVisor APIs and a prelinked binary blob, the uVisor *core*. -It is the responsibility of the uVisor library owner to compile uVisor from its main repository, [ARMmbed/uvisor](https://github.com/ARMmbed/uvisor), and to distribute the resulting libraries to the target operating system. In mbed OS, we periodically compile the uVisor core and release it as a static library for each of the supported targets. These libraries are located in the uVisor folder in the [ARMmbed/mbed-os](https://github.com/ARMmbed/mbed-os) repository. +It is the responsibility of the uVisor library owner to compile uVisor from its main repository, [ARMmbed/uvisor](https://github.com/ARMmbed/uvisor), and to distribute the resulting libraries to the target operating system. In mbed OS, we periodically compile the uVisor core and release it as a static library for each of the supported targets. These libraries are in the uVisor folder in the [ARMmbed/mbed-os](https://github.com/ARMmbed/mbed-os) repository. -This guide will show you a different way of obtaining the uVisor libraries, that is, by building them locally. +This guide will show you a different way of obtaining the uVisor libraries, by building them locally. -There are several reasons why you might want to build uVisor locally: +There are several reasons to build uVisor locally: -* Reproduce a publicly released build. -* Make modifications and test them before contributing to uVisor on GitHub. Read our [contribution guidelines](../../CONTRIBUTING.md) for more details. -* Preview the latest uVisor features before they are packaged and released. -* Play and experiment with uVisor. +- Reproduce a publicly released build. +- Make modifications and test them before contributing to uVisor on GitHub. Read the [contribution guidelines](../../CONTRIBUTING.md) for more details. +- Preview the latest uVisor features before they are packaged and released. +- Play and experiment with uVisor. -You will need the following: +You will need: -* A [target supported](../../README.md#supported-platforms) by uVisor on mbed OS. If your target is not supported yet, you can follow the [uVisor Porting Guide for mbed OS](PORTING.md). -* The Launchpad [GCC ARM Embedded](https://launchpad.net/gcc-arm-embedded) toolchain. -* GNU make. -* git. +- A [target](../../README.md#supported-platforms) uVisor supports on mbed OS. If your target is not supported yet, you can follow the [uVisor porting guide for mbed OS](PORTING.md). +- The Launchpad [GNU ARM Embedded](https://launchpad.net/gcc-arm-embedded) Toolchain. +- GNU Make. +- Git. ## Prepare your application -If you are just starting to look at uVisor and don't have your own application for mbed OS, we suggest you use our example app: +If you are just starting to look at uVisor and don't have your own application for mbed OS, you can use the example app: ```bash $ cd ~/code $ mbed import https://github.com/ARMmbed/mbed-os-example-uvisor ``` -The command above will import all the example dependencies as well, including the mbed OS codebase and the uVisor libraries. +The command above will import all the example dependencies, as well, including the mbed OS codebase and the uVisor libraries. -If you already have an application that you want to use for this guide, make sure that it is ready to work with uVisor enabled. You can follow the [Quick-Start Guide for uVisor on mbed OS](../api/QUICKSTART.md) for more details. +If you already have an application that you want to use for this guide, make sure it is ready to work with uVisor enabled. Follow the [Getting started guide for uVisor on mbed OS](../api/QUICKSTART.md) for more details. In either case, move to the app folder: @@ -39,23 +39,21 @@ In either case, move to the app folder: $ cd ~/code/${your_app} # or ~/code/mbed-os-example-uvisor ``` -## Build the uVisor locally +## Build uVisor locally -By default, the version of mbed OS that you download when you import a program or clone the [ARMmbed/mbed-os](https://github.com/ARMmbed/mbed-os) repository contains a pre-linked version of the uVisor core and public APIs. The uVisor module comes with an importer script, though, that can be run to download the latest uVisor version and compile it. - -You can run the importer script by calling running `make` from the app folder: +By default, the version of mbed OS that you download when you import a program or clone the [ARMmbed/mbed-os](https://github.com/ARMmbed/mbed-os) repository contains a prelinked version of the uVisor core and public APIs. The uVisor module comes with an importer script, though, that you can run to download the latest uVisor version and compile it. You can run the importer script by running `make` from the app folder: ```bash $ make -C ~/code/${your_app}/mbed-os/features/FEATURE_UVISOR/importer ``` -The script will perform the following operations: +The script: -* It fetches the `HEAD` of the release branch of uVisor. -* It compiles the downloaded version of uVisor for all targets, all configurations, in both debug and release mode. -* It copies the libraries and relevant header files from the uVisor repository to the mbed OS folders. +- Fetches the `HEAD` of the release branch of uVisor. +- Compiles the downloaded version of uVisor for all targets and all configurations, in both debug and release mode. +- Copies the libraries and relevant header files from the uVisor repository to the mbed OS folders. -Your local version of the uVisor libraries is now built locally using the latest uVisor version. +You now have built the uVisor libraries locally with the latest uVisor version. ## Build the application @@ -65,19 +63,19 @@ Go back to the application folder: $ cd ~/code/${your_app} ``` -You can now build the application using the latest uVisor version: +You now can build the application using the latest uVisor version: ```bash $ mbed compile -m ${your_target} -t GCC_ARM ``` -Whenever you want to make a change to uVisor, just run `make` again in the importer folder. The change will be automatically propagated to the application: +Whenever you want to make a change to uVisor, just run `make` again in the importer folder. The change automatically propagates to the application: ```bash $ make -C ~/code/${your_app}/mbed-os/core/features/FEATURE_UVISOR/importer ``` -Your application binary will be located in `BUILD/${your_target}/GCC_ARM/${your_app}.bin`. You can drag & drop it to the board if it supports it, or use an external debugger to flash the device. +Your application binary is located in `BUILD/${your_target}/GCC_ARM/${your_app}.bin`. You can drag and drop it to the board if it supports it or use an external debugger to flash the device. ## Debugging uVisor @@ -87,4 +85,4 @@ You can also compile the application using the uVisor debug build: $ mbed compile -m ${your_target} -t GCC_ARM --profile mbed-os/tools/profiles/debug.json ``` -The uVisor debug build gives you access to runtime messages and fault blue screens, which are very useful to understand the uVisor protection mechanisms, but it requires a debugger to be connected to the board. Please read the [Debugging uVisor on mbed OS](../api/DEBUGGING.md) guide for further details. +The uVisor debug build gives you access to runtime messages and fault blue screens, which are useful in understanding the uVisor protection mechanisms, but it requires a debugger to be connected to the board. Please read [Debugging uVisor on mbed OS](../api/DEBUGGING.md) for further details. \ No newline at end of file diff --git a/docs/core/PORTING.md b/docs/core/PORTING.md index 36d3c553..a4090944 100644 --- a/docs/core/PORTING.md +++ b/docs/core/PORTING.md @@ -1,46 +1,36 @@ -# uVisor Porting Guide for mbed OS +# uVisor porting guide for mbed OS -The uVisor is a low-level security module that creates sandboxed environments on ARM Cortex-M3 and ARM Cortex-M4 devices. Each sandbox can get hold of a set of private resources for which uVisor grants exclusive access. Calls into sandboxed APIs are only possible through uVisor-managed entry points. - -The uVisor is not compiled and linked directly into your application. Instead, a glue-layer module compiles the uVisor in the form of a static library, which is periodically released. This library contains the implementation of all the uVisor APIs and the uVisor core, which is a pre-linked binary component. You can also generate these libraries yourself by cloning the open source code-base at [ARMmbed/uvisor](https://github.com/ARMmbed/uvisor) and compiling them using the provided `Makefile`. - -This guide will help you to port uVisor to your platform and to integrate it in mbed OS and CMSIS RTOS. For porting uVisor to other operating systems, please [contact us](mailto:partnership@mbed.com). +This guide will help you port uVisor to your platform and to integrate it in mbed OS and CMSIS RTOS. For porting uVisor to other operating systems, please [contact us](mailto:partnership@mbed.com). ## Overview -This document will cover the following: - -* A quick presentation of the uVisor [repository structure](#repository-structure). -* A [step-by-step](#porting-steps) porting guide to bring uVisor to your platform. -* How to [integrate](#integrate-uvisor-in-mbed-os) your new port of uVisor into mbed OS. +This guide assumes that you are porting a whole device family, called `${family}`, to uVisor. We strongly suggest you abstract and generalize your port as much as possible. Because a static library is generated for each of your family configurations, a more generalized uVisor port results in the generation — and maintenance — of fewer release libraries. You will also enjoy the benefit of going through the porting process only once for a large set of devices. -For the remainder of this guide we will assume that you are porting a whole device family to uVisor, called `${family}`. We strongly suggest to abstract and generalize your port as much as possible. Since a static library is generated for each of your family configurations, a more generalized uVisor port results in the generation — and maintenance — of fewer release libraries. You will also enjoy the nice benefit of going through the porting process only once for a large set of devices. +You will need: -You will need the following: +- The Launchpad GNU ARM Embedded Toolchain. +- GNU Make. +- Git. -* The Launchpad GCC ARM embedded toolchain. -* GNU make. -* git. - -## Repository Structure +## Repository structure [Go to top](#overview) The uVisor is developed in the [ARMmbed/uvisor](https://github.com/ARMmbed/uvisor) repository. Most of your porting efforts will happen there. -Although uVisor is highly self-contained, it still requires some support from the target operating system. There are two files in particular that need to be changed in your code-base: +Although uVisor is highly self-contained, it still requires some support from the target operating system. There are three files in particular that you need to change in your codebase: -1. **Library glue layer**. The [ARMmbed/uvisor](https://github.com/ARMmbed/uvisor) `Makefile` allows you to build both release and debug libraries for all family configurations. The logic to generate this libraries, publish them, and then pick the right library for the right target at build time is OS-specific. This logic must be provided by a glue layer. -1. **Linker script**. It contains specific memory regions and symbols that uVisor relies on. -1. **Start-up script**. uVisor boots right after the system basic initialization, and before the C/C++ library initialization. The start-up script needs to call `uvisor_init()` in between those two. +- **Library glue layer**. This glue-layer module compiles the uVisor in the form of a static library that contains the implementation of the uVisor APIs and the uVisor core, which is a prelinked binary component. You can also build both release and debug libraries for all family configurations by cloning the open source codebase at the [ARMmbed/uvisor](https://github.com/ARMmbed/uvisor) `Makefile`. The logic to generate the libraries, publish them and choose the correct library for the target at build time is OS-specific. A glue layer must provide this logic. +- **Linker script**. It contains specific memory regions and symbols that uVisor relies on. +- **Startup script**. uVisor boots right after the system basic initialization and before the C/C++ library initialization. The startup script needs to call `uvisor_init()` in between those two. -If you are porting uVisor to mbed OS, you will find that the library glue layer is already embedded in the [mbed OS code-base](https://github.com/ARMmbed/uvisor-lib). The linker script and start-up code also live in the same repository. We will guide you through the modifications needed in those files later in this guide. +The library glue layer is already embedded in the [mbed OS codebase](https://github.com/ARMmbed/uvisor-lib). The linker script and startup code are also in that repository. This guide will show you how to modify those files later. -Finally, we provide an example application in [ARMmbed/mbed-os-example-uvisor](https://github.com/ARMmbed/mbed-os-example-uvisor) that shows the minimum set of uVisor features on our supported targets. It can be used during the porting process as a quick way of testing that the uVisor is working as expected on your new platform. +The example application in [ARMmbed/mbed-os-example-uvisor](https://github.com/ARMmbed/mbed-os-example-uvisor) shows the minimum set of uVisor features on the supported targets. You can use it during the porting process as a quick way to test that the uVisor is working on the new platform. ## Porting steps [Go to top](#overview) -We will assume that you are developing in `~/code/`. Clone the uVisor GitHub repository locally: +We assume that you are developing in `~/code/`. Clone the uVisor GitHub repository locally: ```bash $ cd ~/code @@ -50,7 +40,7 @@ $ git clone git@github.com:ARMMbed/uvisor.git ### The uVisor configurations [Go to top](#overview) -A single family of micro-controllers might trigger different releases of uVisor. Although we strive to keep uVisor as hardware-agnostic as possible, there are still some hardware-specific features that we need to take into account. These features are described in the table below. +A single family of microcontrollers might trigger different releases of uVisor. Although uVisor is as hardware-agnostic as possible, there are still some hardware-specific features that you need to know. This table describes these features. | Symbol | Description | |------------------- |----------------------------------------------------------------------------| @@ -61,19 +51,19 @@ A single family of micro-controllers might trigger different releases of uVisor. | `FLASH_LENGTH_MIN` | min( [`FLASH_LENGTH(i)` for `i` in family's devices] ) | | `SRAM_LENGTH_MIN` | min( [`SRAM_LENGTH(i)` for `i` in family's devices] ) | | `NVIC_VECTORS` | max( [`NVIC_VECTORS(i)` for `i` in family's devices] ) | -| `CORE_*` | Core version (e.g. `CORE_CORTEX_M3`) | +| `CORE_*` | Core version (for example, `CORE_CORTEX_M3`) | **Table 1**. Hardware-specific features that differentiate uVisor builds -> **Note**: What we here refer to as "SRAM" is the read/write-able memory that uVisor uses to put its own protected assets. On some platforms this may be a different memory from the physical SRAM, like a tightly-coupled memory (TCM). +> **Note**: "SRAM" is the read/write-able memory that uVisor uses to put its own protected assets. On some platforms, this may be a different memory from the physical SRAM, like a tightly-coupled memory (TCM). -A uVisor *configuration* is defined as the unique combination of the parameters of Table 1. When porting your family to uVisor, you need to make sure that as many library releases as the possible configurations are generated. Let's use an example. +A uVisor *configuration* is the unique combination of the parameters of Table 1. When porting your family to uVisor, make sure that you generate as many library releases as the possible configurations. --- #### Example -Let's assume for simplicity that the `${family}` that you want to port is made of only 4 devices. These have the following values from Table 1: +Assume for simplicity that the `${family}` that you want to port consists of four devices. These have the following values from Table 1: | Symbol | `${device0}` | `${device1}` | `${device2}` | `${device3}` | |-------------------|--------------|--------------|--------------|--------------| @@ -88,13 +78,13 @@ Let's assume for simplicity that the `${family}` that you want to port is made o **Table 2**. Example uVisor configuration values -Following the descriptions of Table 1, some values are common among the 4 devices: +Following the descriptions of Table 1, some values are common among the four devices: -* `NVIC_VECTORS` is the maximum `NVIC_VECTORS(i)`, hence it is 122. -* `FLASH_LENGTH_MIN` and `SRAM_LENGTH_MIN` are 0x80000 and 0x10000, respectively. -* `FLASH_ORIGIN`, `FLASH_OFFSET` and `SRAM_OFFSET` are the same for all the devices, so they will be common to all configurations. +- `NVIC_VECTORS` is the maximum `NVIC_VECTORS(i)`, hence it is 122. +- `FLASH_LENGTH_MIN` is 0x80000, and `SRAM_LENGTH_MIN` is 0x10000. +- `FLASH_ORIGIN`, `FLASH_OFFSET` and `SRAM_OFFSET` are the same for all the devices, so they are common to all configurations. -The remaining values must be combined to form distinct configurations. In this case we only need to combine `SRAM_ORIGIN` and `CORE_*`. If you look at the table above, you will see that they appear in 2 out of the 4 possible value combinations. Hence, we have a total of 2 uVisor configurations. We call them after the parameters that make them unique: +You must combine the remaining values to form distinct configurations. In this case, you only need to combine `SRAM_ORIGIN` and `CORE_*`. If you look at the table, you will see that they appear in two out of the four possible value combinations. Hence, you have a total of two uVisor configurations. Call them after the parameters that make them unique: ```bash CONFIGURATION_0x20000000_CORTEX_M4 = {0x0, 0x400, 0x20000000, 0x400, 0x80000, 0x10000, 122, CORE_CORTEX_M4} @@ -108,11 +98,7 @@ CONFIGURATION_0x1FFF0000_CORTEX_M3 = {0x0, 0x400, 0x1FFF0000, 0x400, 0x80000, 0x ### Memory architecture [Go to top](#overview) -TODO. - -The uVisor expects a distinction between at least two memories: Flash and SRAM. If more than one memory fits this description, you should make an architectural decision about where to put uVisor. We suggest that if you have fast memories you should use those, provided that they offer security features at least equal to those of the regular memories. - -Using a separate read/write-able memory for uVisor (e.g. a TCM) has little impact on the porting process. When relevant, you will find different instructions for this case throughout this document. +The uVisor expects a distinction between at least two memories: Flash and SRAM. If more than one memory fits this description, you should make an architectural decision about where to put uVisor. We suggest that if you have fast memories, you use those, provided they offer security features at least equal to those of the regular memories. ### Platform-specific code [Go to top](#overview) @@ -141,7 +127,7 @@ uvisor    └── ${family} # The release libraries end up here. ``` -Each `[*]` indicates a file you must create during the porting process. Details for each of them are presented below. The snippets that we show refer to the configurations discussed in the previous example. +Each `[*]` indicates a file you must create during the porting process. See details for each of them below. The snippets refer to the configurations discussed in the previous example. --- @@ -149,7 +135,7 @@ Each `[*]` indicates a file you must create during the porting process. Details ~/code/uvisor/platform/${family}/inc/configurations.h ``` -This file contains the uVisor configurations for your family. Remember that each configuration triggers a separate library release. This file should be auto-generated. Symbols that are common to all devices in the family should go first. Configuration-specific symbols are conditionally defined. The condition is based on a macro definition of the form `CONFIGURATION_${CONFIGURATION_NAME}`. +This file contains the uVisor configurations for your family. Each configuration triggers a separate library release. This file should be autogenerated. Symbols that are common to all devices in the family should go first. Configuration-specific symbols are conditionally defined. The condition is based on a macro definition of the form `CONFIGURATION_${CONFIGURATION_NAME}`. **Example** @@ -173,7 +159,7 @@ This file contains the uVisor configurations for your family. Remember that each #define FLASH_LENGTH_MIN 0x80000 #define SRAM_LENGTH_MIN 0x10000 -/* The symbols below can be either configuration-specific or family-wide, +/* The symbols below can be configuration-specific or family-wide, * depending on your requirements. See the porting guide for more details. */ /* Memory boundaries */ @@ -222,9 +208,9 @@ This file contains the uVisor configurations for your family. Remember that each ~/code/uvisor/platform/${family}/inc/config.h ``` -This file contains uVisor customizations that are not hardware-specific but can be chosen by each family (e.g. the default stack size). +This file contains uVisor customizations that are not hardware-specific but can be chosen by each family (for example, the default stack size). -The symbols that you can specify here are listed in the table below. +This table lists symbols that you can specify. | Symbol | Description | |-------------------------------|--------------------------------| @@ -241,7 +227,7 @@ The symbols that you can specify here are listed in the table below. **Table 3**. Optional hardware-specific `config.h` symbols -**Note**: You must always have a separate `configurations.h` file, even if the remaining `config.h` is empty. This ensures that `configurations.h` (which is possibly auto-generated by a script of yours) does not need to know anything more than the features of Table 1. +**Note**: You must always have a separate `configurations.h` file, even if the remaining `config.h` is empty. This ensures that `configurations.h` (which is possibly autogenerated by a script of yours) does not need to know anything more than the features of Table 1. **Example** @@ -263,7 +249,7 @@ The symbols that you can specify here are listed in the table below. ~/code/uvisor/platform/${family}/Makefile.configurations ``` -This file configures the build system for your device family. The table below shows the symbols that can be defined. +This file configures the build system for your device family. This table shows the symbols you can define. | Symbol | Description | |------------------|---------------------------------------------------------------------------| @@ -294,30 +280,30 @@ $ cd ~/code/uvisor $ make ``` -The build process generates as many static libraries (`*.a` files) as your family configurations, multiplied by 2 (debug and release builds). You can find them in `~/code/uvisor/api/lib/${family}`. Please note that these libraries are not published in the uVisor repository. Instead, it is the glue-layer library that deploys them and makes them available to the target OS. +The build process generates as many static libraries (`*.a` files) as your family configurations, multiplied by two (debug and release builds). You can find them in `~/code/uvisor/api/lib/${family}`. Please note that these libraries are not published in the uVisor repository. Instead, the glue-layer library deploys them and makes them available to the target OS. ## Integrate uVisor in mbed OS [Go to top](#overview) -You now need to integrate uVisor in the mbed OS code-base for your target. This requires the following steps, which we will cover in detail: +You now need to integrate uVisor in the mbed OS codebase for your target. This requires the following steps, which we will cover in detail: -* Add a hook to `uvisor_init()` in your start-up script. -* Add the uVisor-specific sections to your platforms' linker scripts. -* Deploy the uVisor libraries for your target platforms. -* Enable uVisor in your targets. +- Add a hook to `uvisor_init()` in your startup script. +- Add the uVisor-specific sections to your platforms' linker scripts. +- Deploy the uVisor libraries for your target platforms. +- Enable uVisor in your targets. -In the sections below, we refer to the mbed OS code-base as shown in the [ARMmbed/mbed-os](https://github.com/ARMmbed/mbed-os) repository. +In the sections below, we refer to the mbed OS codebase as shown in the [ARMmbed/mbed-os](https://github.com/ARMmbed/mbed-os) repository. -### Start-up script +### Startup script [Go to top](#overview) -Assuming that you already ported your platform to mbed, the start-up script usually lives in: +You can usually find the startup script at: ```bash targets/TARGET_${vendor}/TARGET_${family}/TARGET_${device}/device/TOOLCHAIN_${toolchain} ``` -The start-up code must call the function `uvisor_init()` right after system initialization (usually called `SystemInit()`) and right before the C/C++ library initialization. +The startup code must call the function `uvisor_init()` right after system initialization (usually called `SystemInit()`) and right before the C/C++ library initialization. ```C ResetHandler: @@ -333,27 +319,27 @@ ResetHandler: ... ``` -Make sure that no static initialization (zeroing the BSS section, loading data from flash to SRAM) happens before the uVisor initialization. Even setting a single global variable before `uvisor_init()` and then referring to it later on might result in data corruption. +Make sure that no static initialization (zeroing the BSS section, loading data from flash to SRAM) happens before the uVisor initialization. Even setting a single global variable before `uvisor_init()` and then referring to it later can result in data corruption. -The conditional guards that we used in the example above rely on the `FEATURE_UVISOR` and `UVISOR_SUPPORTED` symbols, which will be covered shortly. +The conditional guards in the example above rely on the `FEATURE_UVISOR` and `UVISOR_SUPPORTED` symbols, which this guide will cover in the [Library deployment section](#library-deployment). ### Linker script [Go to top](#overview) -> Note: Since currently uVisor only supports the [GCC ARM Embedded](https://launchpad.net/gcc-arm-embedded) toolchain, only instructions for the GCC `ld` linker script are provided. +> Note: Because uVisor only supports the [GNU ARM Embedded](https://launchpad.net/gcc-arm-embedded) Toolchain, this guide only provides instructions for the GCC `ld` linker script. -To enforce the security model, we need to place uVisor at a specific location in memory, and give uVisor specific information about the rest of the memory map. These symbols live in the `ld` linker script. +To enforce the security model, place uVisor at a specific location in memory and give uVisor specific information about the rest of the memory map. These symbols live in the `ld` linker script. -The snippet below can be used as a template for your `ld` linker script. +You can use the snippet below as a template for your `ld` linker script. -Please note that every occurrence of `...` identifies parts of the existing linker script that have been omitted here for clarity. You will find details and requirements for each section in a table below. +Please note that every occurrence of `...` identifies part of the existing linker script that has been omitted here for clarity. You can find details and requirements for each section in a table below. The linker script template below relies on the following assumptions: -* The device has one flash memory, which is shared between the uVisor and the rest of the OS/app. -* The device has one SRAM memory. - * This can be shared between the uVisor and the OS/app. - * Alternatively, uVisor can be placed in a separate, faster memory (e.g. a TCM). +- The device has one flash memory, which the uVisor and the rest of the OS/app share. +- The device has one SRAM memory. + - The uVisor and the OS/app can share it. + - Alternatively, you can place uVisor in a separate, faster memory (for example, a TCM). ```ld ... @@ -370,7 +356,7 @@ MEMORY /* Define the output sections. */ SECTIONS { - /* The start-up code goes first into the internal flash. */ + /* The startup code goes first into the internal flash. */ .interrupts : { ... @@ -557,7 +543,7 @@ SECTIONS } ``` -As shown in the snippet above, the uVisor needs the following regions: +As the snippet above shows, the uVisor needs the following regions: @@ -570,7 +556,7 @@ As shown in the snippet above, the uVisor needs the following regions: .uvisor.main @@ -578,7 +564,7 @@ As shown in the snippet above, the uVisor needs the following regions: .uvisor.bss @@ -586,7 +572,7 @@ As shown in the snippet above, the uVisor needs the following regions: .page_heap @@ -594,7 +580,7 @@ As shown in the snippet above, the uVisor needs the following regions: .uvisor.secure @@ -602,26 +588,26 @@ As shown in the snippet above, the uVisor needs the following regions: .uninitialized
- It holds the monolithic uVisor core binary. Its location determines the API table of uVisor, since uvisor_api points to the first entry in this table. This region must be located at the same offset that you specified as FLASH_OFFSET in your configuration. + It holds the monolithic uVisor core binary. Its location determines the API table of uVisor because uvisor_api points to the first entry in this table. This region must be located at the same offset as FLASH_OFFSET in your configuration.
- It contains both uVisor's own memories (coming from the uVisor library, in .keep.uvisor.bss.main) and the private boxes' protected memories (in .keep.uvisor.bss.boxes). This region must be located at the same offset that you specified in SRAM_OFFSET. If uVisor shares the SRAM with the OS/app, this region must be positioned right before the page heap section, and immediately after the VTOR relocation section. Otherwise, it spans the whole memory devoted to it (e.g. a TCM). + It contains uVisor's memories (coming from the uVisor library, in .keep.uvisor.bss.main) and the private boxes' protected memories (in .keep.uvisor.bss.boxes). This region must be located at the same offset that you specified in SRAM_OFFSET. If uVisor shares the SRAM with the OS/app, this region must be positioned right before the page heap section and immediately after the VTOR relocation section. Otherwise, it spans the whole memory devoted to it (for example, a TCM).
- It is needed to host pages allocated by the uVisor secure allocator. If the uVisor shares the SRAM with the OS/app, it must be placed right after the uVisor BSS section, otherwise it must be the first SRAM region after the VTOR relocation section. + It hosts pages the uVisor secure allocator allocates. If the uVisor shares the SRAM with the OS/app, it must be immediately after the uVisor BSS section. Otherwise, it must be the first SRAM region after the VTOR relocation section.
- It contains constant data in flash that describes the configuration of the secure boxes. It comprises the configuration tables (.keep.uvisor.cfgtbl) and the pointers to them (.keep.uvisor.cfgtbl_ptr[_first]), which are used by uVisor for box enumeration. + It contains constant data in flash that describes the configuration of the secure boxes. It comprises the configuration tables (.keep.uvisor.cfgtbl) and the pointers to them (.keep.uvisor.cfgtbl_ptr[_first]), which uVisor uses for box enumeration.
- It is not strictly speaking a uVisor region, but this should go in the linker script to make sure that data can be passed to the execution environment across soft reboots. This section is never touched by the C/C++ library initialization, and it's used by our testing framework. + It is not, strictly speaking, a uVisor region but should go in the linker script, so data can pass to the execution environment across soft reboots. The C/C++ library initialization never touches this section, but the testing framework uses it.
-All these sections and their sub-regions must be preceded and followed by symbols that describe their boundaries, as shown in the template. Note that all symbols prefixed with `.keep` must end up in the final memory map even if they are not explicitly used. +All these sections and their subregions must start and end with symbols that describe their boundaries, as the template shows. Note that all symbols prefixed with `.keep` must end up in the final memory map even if they are not explicitly used. -Once uVisor is active it maintains its own vector table, which the rest of the code can only interact with through uVisor APIs. As shown in the script above, we still suggest to leave the standard vector table in flash, so that if uVisor is disabled any legacy mechanism for interrupt management still works. This is the same reason why we need an `SRAM_OFFSET` as well, as it reserves the space for relocation of the original OS vector table. +Once uVisor is active, it maintains its own vector table, which the rest of the code can only interact with through uVisor APIs. As the script above shows, we still suggest to leave the standard vector table in flash so that if uVisor is disabled, any legacy mechanism for interrupt management still works. This is the same reason we need an `SRAM_OFFSET`; it reserves the space for relocation of the original OS vector table. -The heap boundaries need to be provided (`__uvisor_heap_start` and `__uvisor_heap_end`), as they are used by the uVisor allocator APIs. The uVisor allocator APIs are available even when uVisor is not strictly supported on a platform, although in that case no security feature is provided. See below for more information. +The heap boundaries need to be provided (`__uvisor_heap_start` and `__uvisor_heap_end`) because the uVisor allocator APIs use them. The uVisor allocator APIs are available even when uVisor is not strictly supported on a platform, but in that case, there is no security feature. -> **Note**: After making the necessary changes to your linker script, `HEAP_SIZE` becomes the minimum heap size. The heap will grow to fill all available RAM between the stack and data sections. If there is not enough room for the minimum heap size, the linker will generate an error. The reason for this change is to ease transitioning applications from the legacy heap to the [uVisor page heap](https://github.com/ARMmbed/uvisor/blob/master/docs/api/manual/UseCases.md#tier-1-page-allocator); after an application is fully transitioned to the [uVisor page heap](https://github.com/ARMmbed/uvisor/blob/master/docs/api/manual/UseCases.md#tier-1-page-allocator), the legacy heap is no longer required and can have a `HEAP_SIZE` size of 0. +> **Note**: After making the necessary changes to your linker script, `HEAP_SIZE` becomes the minimum heap size. The heap will grow to fill all available RAM between the stack and data sections. If there is not enough room for the minimum heap size, the linker generates an error. The reason for this change is to ease transitioning applications from the legacy heap to the [uVisor page heap](https://github.com/ARMmbed/uvisor/blob/master/docs/api/manual/UseCases.md#tier-1-page-allocator); after an application is fully transitioned to the [uVisor page heap](https://github.com/ARMmbed/uvisor/blob/master/docs/api/manual/UseCases.md#tier-1-page-allocator), the legacy heap is no longer required and can have a `HEAP_SIZE` size of 0. -Finally, note that at the end of the linker script the physical boundaries for the memories (flash/SRAM) populated by uVisor are also provided. +Finally, note that at the end of the linker script, the physical boundaries for the memories (flash/SRAM) uVisor populates are also provided. ### Library deployment [Go to top](#overview) -The uVisor code-base is embedded in [ARMmbed/mbed-os](https://github.com/ARMmbed/mbed-os), and can be found at `features/FEATURE_UVISOR`. This module is a glue layer library that translates the [ARMmbed/uvisor](https://github.com/ARMmbed/uvisor) core and APIs into a file structure that is compatible with mbed OS: +The uVisor codebase is embedded in [ARMmbed/mbed-os](https://github.com/ARMmbed/mbed-os), and you can find it at `features/FEATURE_UVISOR`. This module is a glue layer library that translates the [ARMmbed/uvisor](https://github.com/ARMmbed/uvisor) core and APIs into a file structure that is compatible with mbed OS: ``` mbed @@ -644,13 +630,9 @@ mbed └── TARGET_UVISOR_UNSUPPORTED ``` -The uVisor repository is included as a sub-module in the -``` -features/FEATURE_UVISOR/importer/TARGET_IGNORE -``` -folder. As the folder name suggests, the uVisor code is never compiled directly. Instead, an importer script, `Makefile`, is used to periodically build the uVisor and to publish the resulting libraries in the `source` and `includes` directories. +The uVisor repository is a submodule in the `features/FEATURE_UVISOR/importer/TARGET_IGNORE` folder. As the folder name suggests, the uVisor code is never compiled directly. Instead, an importer script, `Makefile`, periodically builds the uVisor and publishes the resulting libraries in the `source` and `includes` directories. -If you want to add your newly ported platforms to this deployment process, you need to change the `Makefile` so that it knows how to translate the `${family}` name that you used during the porting process into a compatible mbed OS target name. Given the generality of your uVisor port, you might need to translate the same family/configuration to multiple mbed OS targets. +If you want to add your newly ported platforms to this deployment process, you need to change the `Makefile`, so it knows how to translate the `${family}` name that you used during the porting process into a compatible mbed OS target name. Given the generality of your uVisor port, you might need to translate the same family/configuration to multiple mbed OS targets. The translation requires you to add an element to the following line in the `Makefile`: @@ -658,26 +640,26 @@ The translation requires you to add an element to the following line in the `Mak TARGET_TRANSLATION:=MCU_K64F.kinetis EFM32.efm32 STM32F4.stm32 ``` -Following the previous examples in this porting guide, we assumed that your device `${family}` contains four devices, `${device0}`, `${device1}`, `${device2}`, and `${device3}`. The first two belong to the `CONFIGURATION_0x20000000_CORTEX_M4` configuration, the other two to the `CONFIGURATION_0x1FFF0000_CORTEX_M3` one. +Following the previous examples in this porting guide, we assumed that your device `${family}` contains four devices, `${device0}`, `${device1}`, `${device2}` and `${device3}`. The first two belong to the `CONFIGURATION_0x20000000_CORTEX_M4` configuration, the other two to the `CONFIGURATION_0x1FFF0000_CORTEX_M3` one. -If we assume that in mbed OS you have two targets named `TARGET_${device0_or_1}`, `TARGET_${device2_or_3}` then the `Makefile` translation will look like the following: +If you have two targets named `TARGET_${device0_or_1}` and `TARGET_${device2_or_3}` in mbed OS, the `Makefile` translation will look like: ```make TARGET_TRANSLATION:=MCU_K64F.kinetis EFM32.efm32 STM32F4.stm32 ${device0_or_1}.${family} ${device2_or_3}.family ``` -Once the importer script has been updated, you can run `make` from the importer folder. Your new libraries will show up in the `targets` folder. +Once you have updated the importer script, you can run `make` from the importer folder. Your new libraries will show up in the `targets` folder. ### Target configuration [Go to top](#overview) -Although the uVisor code-base is always fetched when downloading the mbed OS repository, its libraries are never linked automatically. In order to do so, the target description must explicitly specify that uVisor is a supported feature for that target. +Although downloading the mbed OS repository always fetches the uVisor codebase, the uVisor libraries do not link automatically. For them to do so, the target description must explicitly specify that uVisor is a supported feature for that target. -In addition, even when the uVisor feature is set, a low-impact version of the uVisor libraries is linked by default, which does not enable the uVisor security features. We call this version of the libraries the *unsupported* libraries. +In addition, even when the uVisor feature is set, a low-impact version of the uVisor libraries links by default, which does not enable the uVisor security features. We call this version of the libraries the *unsupported* libraries. -The unsupported libraries are useful when an application or the operating system want to use uVisor APIs that are available even when the uVisor is not supported on a platform. At the moment, only the uVisor allocator APIs are available in the unsupported libraries. +The unsupported libraries are useful when an application or the operating system wants to use uVisor APIs that are available even when a platform does not support the uVisor. At the moment, only the uVisor allocator APIs are available in the unsupported libraries. -To enable the uVisor feature in a target and trigger the linkage of the supported libraries, see the table below. +To enable the uVisor feature in a target and trigger the linkage of the supported libraries, see this table. @@ -696,7 +678,7 @@ To enable the uVisor feature in a target and trigger the linkage of the supporte Add UVISOR to the features field in the target description file. @@ -722,11 +704,11 @@ Given the description above, the following combinations are possible: | Defined | Not defined | uVisor is linked in your app in *unsupported* mode. Only the uVisor allocator APIs are available. | | Defined | Defined | uVisor is linked in your app in *supported* mode. All uVisor APIs and security features are available. | -Please note that defining both the `FEATURE_UVISOR` and the `UVISOR_SUPPORTED` symbols does not automatically enable uVisor on the application. By default uVisor runs in *disabled* mode, where the uVisor initialization function is executed but returns immediately. +Please note that defining both the `FEATURE_UVISOR` and the `UVISOR_SUPPORTED` symbols does not automatically enable uVisor on the application. By default, uVisor runs in *disabled* mode, where the uVisor initialization function is executed but returns immediately. -In disabled mode no security feature is enabled, although the uVisor binaries are still flashed to the device. To learn more about the uVisor modes of operation please check out the [API documentation](../api/API.md). If you want to know how to enable uVisor and configure an application to use the uVisor security features, please checkout the [Quick-Start Guide for uVisor on mbed OS](../api/QUICKSTART.md). +In disabled mode, no security feature is enabled though the uVisor binaries are still flashed to the device. To learn more about the uVisor modes of operation, please see the [API documentation](../api/API.md). If you want to know how to enable uVisor and configure an application to use the uVisor security features, please see the [getting started guide](../api/QUICKSTART.md). -If you do not want to enable the uVisor features straight away on your mbed target, users can still selectively override the target features and labels to enable uVisor. The `FEATURE_UVISOR` and `UVISOR_SUPPORTED` symbols can be set using the configuration system, by creating a file called `mbed_app.json` at the application level with the following content: +If you do not want to enable the uVisor features immediately on your mbed target, users can still selectively override the target features and labels to enable uVisor. You can use the configuration system to set the `FEATURE_UVISOR` and `UVISOR_SUPPORTED` symbols by creating a file called `mbed_app.json` at the application level with the following content: ```json { @@ -743,24 +725,14 @@ If you do not want to enable the uVisor features straight away on your mbed targ } ``` -Please note that 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 startup script (usually in assembly) relies on those symbols, we need to define them manually. +Please note that 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 startup script (usually in assembly) relies on those symbols, you need to define them manually. ### Next steps [Go to top](#overview) -If you have followed all the steps in this guide, this is what you should have: - -* Your whole family ported to the [ARMmbed/uvisor](https://github.com/ARMmbed/uvisor) code-base. -* All the uVisor library releases built for your family, saved in `~/code/uvisor/api/lib/${family}`. -* The uVisor libraries integrated in mbed OS: - * The linker script updated. - * The start-up script updated. - * The importer `Makefile` updated and you libraries deployed. - * Your target description updated to link the uVisor libraries. - -It is now time to test your application. We suggest that you use our example app, [`mbed-os-example-uvisor`](https://github.com/ARMmbed/mbed-os-example-uvisor), but if you prefer you can try and build a very simple uVisor-enabled blinky program following the [Quick-Start Guide for uVisor on mbed OS](../api/QUICKSTART.md). +It is time to test your application. We suggest that you use the example app, [`mbed-os-example-uvisor`](https://github.com/ARMmbed/mbed-os-example-uvisor), but if you prefer, you can build a uVisor-enabled blinky program following the [getting started guide](../api/QUICKSTART.md). -In both cases, please make sure to do the following: +In both cases, please: -* Make sure to run uVisor at least once in debug mode to ensure that all runtime sanity checks pass. This round of checks can also be used as a confirmation step, proving that your linker script and uVisor ports are structurally correct. For more information on the uVisor debug mode, please read the [Debugging uVisor on mbed OS](../api/DEBUGGING.md) guide. -* Ensure that your app has the relevant ACLs to work with uVisor enabled. At the moment this requires to run uVisor in debug mode multiple times and find all the faulting peripherals. The procedure is described in greater detail in [the final section](../api/QUICKSTART.md#the-main-box-acls) of the [Quick-Start Guide for uVisor on mbed OS](../api/QUICKSTART.md). +- Run uVisor at least once in debug mode to ensure all runtime sanity checks pass. You can also use this round of checks to confirm your linker script and uVisor ports are structurally correct. For more information about the uVisor debug mode, please read [Debugging uVisor on mbed OS](../api/DEBUGGING.md). +- Ensure that your app has the relevant ACLs to work with uVisor enabled. This requires you to run uVisor in debug mode multiple times and find all the faulting peripherals. Read a more detailed description of this procedure in [the final section](../api/QUICKSTART.md#the-main-box-acls) of the [getting started guide](../api/QUICKSTART.md). \ No newline at end of file
- If a target sets it, the uVisor code-base is compiled and linked when building applications for that target. Depending on whether UVISOR_SUPPORTED is set or not, the linked libraries are in unsupported or supported mode. In unsupported mode no security feature is provided, and only the allocator APIs are available. + If a target sets it, the uVisor codebase is compiled and linked when building applications for that target. Depending on whether UVISOR_SUPPORTED is set, the linked libraries are in unsupported or supported mode. In unsupported mode, there are no security features, and only the allocator APIs are available.