Microvisor is in a pre-release phase and the information contained in this document is subject to change. Some features referenced below may not be fully available until Microvisor's General Availability (GA) release.
If you are looking for the Microvisor REST API for cloud interaction, please see the Microvisor API.
Applications communicate with Microvisor through the Microvisor system calls. Interaction with Microvisor is transactional: the application calls functions to request the operations it would like Microvisor to perform on its behalf because each action involves access to 'secure' resources that the 'non-secure' application doesn't have permission to work with directly.
Many operations that are requested via the system calls take place asynchronously. Other system calls register the application's interest in being informed when key system events take place. Both of these types of call trigger the issue of notifications from Microvisor to the application. Microvisor notifications are the only way the application can learn about the outcome of an operation, or that a certain system event has taken place. The application is alerted to the presence of a new notification by a non-secure interrupt that it specifies when setting up notifications.
A number of other system calls are akin to traditional getters and setters: the application provides Microvisor with data it wants written to, say, a specified microcontroller register, or requests the current contents of that register. These functions are not executed asynchronously. In the case of the getters, they return the requested data by writing it to memory specified in the call.
In fact, no system call returns data directly. Instead they return a value which indicates whether Microvisor accepted the requested operation, or rejected it. Reasons for rejection include not only mis-formed action requests — pointers to memory the application is not permitted to access, for example, or attempts to open more network data channels that is allowed — but also situations in which Microvisor is unable to comply because overriding circumstances prevent it from doing so. A case in point: Microvisor may refuse a request from the application to reboot the host microcontroller because its is downloading a software update chunk.
In C, Microvisor system calls take the following form:
extern enum MvStatus mvFunctionName(...)
All function names are prefixed mv
.
All calls return a case of the enum MvStatus
.
Only the name and list of parameters are function-specific.
The Microvisor system calls initially will provide the application with functionality in the following key areas:
These functions allow applications to configure application logging and to issue log messages. Logged messages are accessed using the Twilio CLI Microvisor plugin.
Application logging is intended for coarse-level feedback and basic debugging. Posted log messages are not guaranteed to be received, and no claim is made as to the order in which they may be delivered. You should not use application logging as a data-delivery mechanism.
Configuration data access calls allows devices to retrieve pre-uploaded values — API keys, PKI certificates and keys, and such — from the Microvisor cloud so that confidential information need not be baked into application firmware.
The device hardware calls provide access to peripheral registers that are owned by Microvisor but provide access to resources that may be used by the application. The application has unmediated access to all other peripherals, i.e., those it does not share with Microvisor.
The remainder of the hardware calls is given over to functions that provide the application with information about its host: for example, the unique ID that identifies the device in the Microvisor cloud and console.
The factory calls are designed to run in the context of the Microvisor factory flow: the special procedure you need to follow to ensure devices on the assembly line are provisioned with Microvisor and your application firmware, and that the device hardware is fully tested before final assembly.
The fast interrupt calls allow developers to minimize the latency of a number of interrupts by instructing Microvisor to permit those interrupts to pre-empt secure threads. This typically reduces latency to under 20μs, but with some other important limitations.
Microvisor's initial networking calls are geared towards the establishment of a network connection between the device and the Microvisor cloud, and the formation and use of bi-directional data channels routed over this connection. This includes the following network operations:
These tasks are asynchronous and make extensive use of Microvisor notifications. As part of the process of requesting a network connection, the application is therefore expected to provide notification storage.
As part of the process of establishing a data channel, the application is also expected to provide storage for the channel's read and write buffers.
Microvisor supports the issue of HTTP requests and processing of the responses they generate. These calls require the establishment of suitable network connections and data channels. Then the application can:
Microvisor supports the issue of MQTT requests and processing of the responses they generate. These calls require the establishment of suitable network connections and data channels. Then the application can:
The notifications calls allow the application to configure how it is informed about system events. For details, see Microvisor notifications.
The timekeeping calls focus on providing the application with timing signals from the host microcontroller's clocks, which are of necessity owned by Microvisor. This includes:
Network and other resources mediated by Microvisor are identified in application-Microvisor interactions by 'handles'. A handle is a 32-bit non-zero integer which is randomly generated and remains unique for the lifetime of the resource to which it has been assigned.
For example, when the application requests a network connection, Microvisor will provide it with handle that identifies the new connection. The application references the connection by its handle when it makes use of the connection — to open data channels over it, for instance — and to subsequently relinquish access to it.
To request a network connection, the application supplies a handle that identifies a notification center that has already been instantiated and which the application wants to dispatch the notifications generated by the new connection.
Before using a handle, you should always check that it is not zero. Microvisor defines an invalid handle as any handle with a value of zero. All extant handles are zeroed when the host microcontroller restarts, and specific handles are zeroed when the resource they refer to is relinquished or closed by the application. If you attempt to make use of an invalid handle, Microvisor will report it as an error.
System calls which ask Microvisor to provide the application with values take a pointer which indicates where the returning value will be written and the number of bytes the application expects to be written. It is the application's responsibility to allocate memory for such storage and to do so in memory to which it has access: Microvisor will reject actions whose configurations require it to write results to memory that is inaccessible to the application.
System calls which ask Microvisor to set values take the value itself. For bulk data, the function takes a pointer to the data in application memory, and the number of bytes to be read.
Microvisor system calls do not return requested data or values directly — they are instead written to memory specified by the application. However, system calls do return a value which indicates whether the action requested by the function call was accepted or rejected by Microvisor.
If Microvisor accepts the request, it returns zero. Any non-zero value therefore represents an error, and the actual value indicates the cause of the error. The application should always check the return value from any call it makes. The final system calls design will enumerate each of the error values a system call may return; this enumeration will be included in the header files for ready access to error values in code.
Example errors include:
Microvisor checks each supplied parameter — and the fields in supplied structures — and will return an error on the first of these that is mis-set. Applications should not assume that an error generated by a single mis-configured parameter means that all other parameters are valid: re-calling a function after correcting one parameter will return another error if a further parameter is found to be bad.
Most system calls make use of a number of pre-defined data structures for passing data to and receiving data from Microvisor.
1struct MvSizedString {2uint8_t *data,3uint32_t length4}
This structure is used to pass in string data. Include a pointer to the string's byte data, and the number of bytes in the string.
1struct MvSizedStringBuffer {2uint8_t *data,3uint32_t size,4uint32_t *length5}
This structure is used to provide Microvisor with a buffer into which it can write string data. Include a pointer to buffer, and the size of the buffer in bytes. On a successful write, Microvisor will record the number of bytes actually written, which will be less than or equal to size
, to the pointer length
.
We welcome all inquiries you may have about Microvisor and its implementation, and any support questions that arise once you've begun developing with Microvisor. Please submit your queries via a KORE Wireless ticket: log in to the Kore console and click the Contact Support button in the left-hand navbar.