Microvisor System Calls
Note
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.

The Microvisor software architecture
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.
System Call Design
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.
System Call Coverage
The Microvisor system calls initially will provide the application with functionality in the following key areas:
Application Logging
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
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.
Device Hardware Operations
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.
Factory Operations
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.
Fast Interrupt Operations
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.
Move interrupts into low-latency mode, or move them out of it.
Activate and deactivate specific low-latency interrupts.
Activate and deactivate all low-latency interrupts at once.
HTTP Operations
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:
Issue HTTP requests.
Determine the status of a completed request.
Read back any of the headers included in the response.
Read back all or part of any body data included in the response.
MQTT Operations
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:
Issue MQTT requests.
Connect to MQTT brokers and subscribe to topics.
Post messages to MQTT topics, and receive messages posted to subscribed topics.
Networking Operations
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:
Request and relinquish a network connection.
Open and close a data channel.
Request to be notified when data has been received through a channel and placed in its receive buffer.
Request to be notified when a channel’s transmit buffer has space for new data to be written to it.
Read and write data from or to a channel via its buffers.
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.
Notification Management
The notifications calls allow the application to configure how it is informed about system events. For details, see Microvisor notifications.
Timekeeping
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:
Request the current value of the system’s monotonic microseconds counter.
Request the current value of the system’s RTC.
Handles
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.
Note
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.
Getters and Setters
Getting Values via System Calls
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.
Setting Values via System Calls
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.
Errors and Return Values
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:
Supplying an IRQ line that is not targeted to non-secure mode.
Supplying an IRQ line that is already in use.
Passing a pointer to data held in memory not accessible by the application.
Passing a pointer to a buffer that references memory not accessible by the application.
A specified buffer size is too small to hold the data that will be written to it.
A requested time can not be provided because the RTC has not yet been set.
A register operation targets an illegal or unavailable register.
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.
Data Structures
Most system calls make use of a number of pre-defined data structures for passing data to and receiving data from Microvisor.
MvSizedString
struct MvSizedString {
uint8_t *data,
uint32_t length
}
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.
MvSizedStringBuffer
struct MvSizedStringBuffer {
uint8_t *data,
uint32_t size,
uint32_t *length
}
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
.